diff --git a/gulpfile.js b/gulpfile.js index 8463487..fa7ff6d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -27,6 +27,9 @@ let paths = { 'src_data/scripts/i18n.min.js', 'src_data/scripts/lang.js', 'src_data/scripts/utils.js' + ], + 'chart.js': [ + 'src_data/scripts/chart.js' ] } }, diff --git a/lib/Equitherm/Equitherm.h b/lib/Equitherm/Equitherm.h deleted file mode 100644 index 287b4f2..0000000 --- a/lib/Equitherm/Equitherm.h +++ /dev/null @@ -1,63 +0,0 @@ -#include - -#if defined(EQUITHERM_INTEGER) -// расчёты с целыми числами -typedef int datatype; -#else -// расчёты с float числами -typedef float datatype; -#endif - -class Equitherm { -public: - datatype targetTemp = 0; - datatype indoorTemp = 0; - datatype outdoorTemp = 0; - float Kn = 0.0; - float Kk = 0.0; - float Kt = 0.0; - - Equitherm() = default; - - // kn, kk, kt - Equitherm(float new_kn, float new_kk, float new_kt) { - Kn = new_kn; - Kk = new_kk; - Kt = new_kt; - } - - // лимит выходной величины - void setLimits(unsigned short min_output, unsigned short max_output) { - _minOut = min_output; - _maxOut = max_output; - } - - // возвращает новое значение при вызове - datatype getResult() { - datatype output = getResultN() + getResultK() + getResultT(); - output = constrain(output, _minOut, _maxOut); // ограничиваем выход - return output; - } - -private: - unsigned short _minOut = 20, _maxOut = 90; - - // температура контура отопления в зависимости от наружной температуры - datatype getResultN() { - float a = (-0.21 * Kn) - 0.06; // a = -0,21k — 0,06 - float b = (6.04 * Kn) + 1.98; // b = 6,04k + 1,98 - float c = (-5.06 * Kn) + 18.06; // с = -5,06k + 18,06 - float x = (-0.2 * outdoorTemp) + 5; // x = -0.2*t1 + 5 - return (a * x * x) + (b * x) + c; // Tn = ax2 + bx + c - } - - // поправка на желаемую комнатную температуру - datatype getResultK() { - return (targetTemp - 20) * Kk; - } - - // Расчет поправки (ошибки) термостата - datatype getResultT() { - return constrain((targetTemp - indoorTemp), -3, 3) * Kt; - } -}; \ No newline at end of file diff --git a/src/HaHelper.h b/src/HaHelper.h index a41454b..3b6f9ba 100644 --- a/src/HaHelper.h +++ b/src/HaHelper.h @@ -844,19 +844,19 @@ public: return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_SWITCH), F("equitherm")).c_str(), doc); } - bool publishInputEquithermFactorN(bool enabledByDefault = true) { + bool publishInputEquithermSlope(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->getUniqueIdWithPrefix(F("equitherm_n")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_n")); + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_slope")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_slope")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_NAME)] = F("Equitherm factor N"); - doc[FPSTR(HA_ICON)] = F("mdi:alpha-n-circle-outline"); + doc[FPSTR(HA_NAME)] = F("Equitherm slope"); + doc[FPSTR(HA_ICON)] = F("mdi:slope-uphill"); doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str(); - doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.n_factor|float(0)|round(3) }}"); + doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.slope|float(0)|round(3) }}"); doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str(); - doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"n_factor\" : {{ value }}}}"); + doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"slope\" : {{ value }}}}"); doc[FPSTR(HA_MIN)] = 0.001f; doc[FPSTR(HA_MAX)] = 10; doc[FPSTR(HA_STEP)] = 0.001f; @@ -864,56 +864,80 @@ public: doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter; doc.shrinkToFit(); - return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_n_factor")).c_str(), doc); + return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_slope")).c_str(), doc); } - bool publishInputEquithermFactorK(bool enabledByDefault = true) { + bool publishInputEquithermExponent(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->getUniqueIdWithPrefix(F("equitherm_k")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_k")); + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_exponent")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_exponent")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_NAME)] = F("Equitherm factor K"); - doc[FPSTR(HA_ICON)] = F("mdi:alpha-k-circle-outline"); + doc[FPSTR(HA_NAME)] = F("Equitherm exponent"); + doc[FPSTR(HA_ICON)] = F("mdi:exponent"); doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str(); - doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.k_factor|float(0)|round(2) }}"); + doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.exponent|float(0)|round(3) }}"); doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str(); - doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"k_factor\" : {{ value }}}}"); - doc[FPSTR(HA_MIN)] = 0; - doc[FPSTR(HA_MAX)] = 10; + doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"exponent\" : {{ value }}}}"); + doc[FPSTR(HA_MIN)] = 0.1; + doc[FPSTR(HA_MAX)] = 2; + doc[FPSTR(HA_STEP)] = 0.001f; + doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX); + doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter; + doc.shrinkToFit(); + + return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_exponent")).c_str(), doc); + } + + bool publishInputEquithermShift(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->getUniqueIdWithPrefix(F("equitherm_shift")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_shift")); + doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); + doc[FPSTR(HA_NAME)] = F("Equitherm shift"); + doc[FPSTR(HA_ICON)] = F("mdi:chart-areaspline"); + doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str(); + doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.shift|float(0)|round(2) }}"); + doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str(); + doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"shift\" : {{ value }}}}"); + doc[FPSTR(HA_MIN)] = -15; + doc[FPSTR(HA_MAX)] = 15; doc[FPSTR(HA_STEP)] = 0.01f; doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX); doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter; doc.shrinkToFit(); - return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_k_factor")).c_str(), doc); + return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_shift")).c_str(), doc); } - bool publishInputEquithermFactorT(bool enabledByDefault = true) { + bool publishInputEquithermTargetDiffFactor(bool enabledByDefault = true) { JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->settingsTopic.c_str(); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.pid.enabled, 'offline', 'online') }}"); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_t")); - doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_t")); + doc[FPSTR(HA_UNIQUE_ID)] = this->getUniqueIdWithPrefix(F("equitherm_target_diff_factor")); + doc[FPSTR(HA_DEFAULT_ENTITY_ID)] = this->getEntityIdWithPrefix(FPSTR(HA_ENTITY_NUMBER), F("equitherm_target_diff_factor")); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_NAME)] = F("Equitherm factor T"); - doc[FPSTR(HA_ICON)] = F("mdi:alpha-t-circle-outline"); + doc[FPSTR(HA_NAME)] = F("Equitherm target diff factor"); + doc[FPSTR(HA_ICON)] = F("mdi:chart-timeline-variant-shimmer"); doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str(); - doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.t_factor|float(0)|round(2) }}"); + doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.targetDiffFactor|float(0)|round(3) }}"); doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str(); - doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"t_factor\" : {{ value }}}}"); + doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"targetDiffFactor\" : {{ value }}}}"); doc[FPSTR(HA_MIN)] = 0; doc[FPSTR(HA_MAX)] = 10; - doc[FPSTR(HA_STEP)] = 0.01f; + doc[FPSTR(HA_STEP)] = 0.001f; doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX); doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter; doc.shrinkToFit(); - return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_t_factor")).c_str(), doc); + return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_target_diff_factor")).c_str(), doc); } diff --git a/src/MqttTask.h b/src/MqttTask.h index 7578f72..b629d78 100644 --- a/src/MqttTask.h +++ b/src/MqttTask.h @@ -502,9 +502,10 @@ protected: // equitherm this->haHelper->publishSwitchEquitherm(); - this->haHelper->publishInputEquithermFactorN(false); - this->haHelper->publishInputEquithermFactorK(false); - this->haHelper->publishInputEquithermFactorT(false); + this->haHelper->publishInputEquithermSlope(false); + this->haHelper->publishInputEquithermExponent(false); + this->haHelper->publishInputEquithermShift(false); + this->haHelper->publishInputEquithermTargetDiffFactor(false); // states this->haHelper->publishStatusState(); diff --git a/src/RegulatorTask.h b/src/RegulatorTask.h index f768a54..4334df7 100644 --- a/src/RegulatorTask.h +++ b/src/RegulatorTask.h @@ -1,7 +1,5 @@ -#include #include -Equitherm etRegulator; GyverPID pidRegulator(0, 0, 0); @@ -146,39 +144,32 @@ protected: // if use equitherm if (settings.equitherm.enabled) { - unsigned short minTemp = settings.heating.minTemp; - unsigned short maxTemp = settings.heating.maxTemp; - float targetTemp = settings.heating.target; - float indoorTemp = vars.master.heating.indoorTemp; - float outdoorTemp = vars.master.heating.outdoorTemp; + float tempDelta = settings.heating.target - vars.master.heating.outdoorTemp; + float maxPoint = settings.heating.target - ( + settings.heating.maxTemp - settings.heating.target + ) / settings.equitherm.slope; - if (settings.system.unitSystem == UnitSystem::IMPERIAL) { - minTemp = f2c(minTemp); - maxTemp = f2c(maxTemp); - targetTemp = f2c(targetTemp); - indoorTemp = f2c(indoorTemp); - outdoorTemp = f2c(outdoorTemp); + float sf = (settings.heating.maxTemp - settings.heating.target) / pow( + settings.heating.target - maxPoint, + 1.0f / settings.equitherm.exponent + ); + float etResult = settings.heating.target + settings.equitherm.shift + sf * ( + tempDelta >= 0 + ? pow(tempDelta, 1.0f / settings.equitherm.exponent) + : -(pow(-(tempDelta), 1.0f / settings.equitherm.exponent)) + ); + + // add diff + if (this->indoorSensorsConnected && !settings.pid.enabled && !settings.heating.turbo) { + etResult += constrain( + settings.heating.target - vars.master.heating.indoorTemp, + -3.0f, + 3.0f + ) * settings.equitherm.targetDiffFactor; } - if (!this->indoorSensorsConnected || settings.pid.enabled) { - etRegulator.Kt = 0.0f; - etRegulator.indoorTemp = 0.0f; - - } else { - etRegulator.Kt = settings.heating.turbo ? 0.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); - } + // limit + etResult = constrain(etResult, settings.heating.minTemp, settings.heating.maxTemp); if (fabsf(prevEtResult - etResult) > 0.09f) { prevEtResult = etResult; diff --git a/src/Settings.h b/src/Settings.h index ecb4d15..c09fb89 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -154,9 +154,10 @@ struct Settings { struct { bool enabled = false; - float n_factor = 0.7f; - float k_factor = 3.0f; - float t_factor = 2.0f; + float slope = 0.7f; + float exponent = 1.3f; + float shift = 0.0f; + float targetDiffFactor = 2.0f; } equitherm; struct { diff --git a/src/strings.h b/src/strings.h index 268d73e..961721e 100644 --- a/src/strings.h +++ b/src/strings.h @@ -81,6 +81,7 @@ const char S_ENABLED[] PROGMEM = "enabled"; const char S_ENV[] PROGMEM = "env"; const char S_EPC[] PROGMEM = "epc"; const char S_EQUITHERM[] PROGMEM = "equitherm"; +const char S_EXPONENT[] PROGMEM = "exponent"; const char S_EXTERNAL_PUMP[] PROGMEM = "externalPump"; const char S_FACTOR[] PROGMEM = "factor"; const char S_FAULT[] PROGMEM = "fault"; @@ -117,7 +118,6 @@ const char S_INVERT_STATE[] PROGMEM = "invertState"; const char S_IP[] PROGMEM = "ip"; const char S_I_FACTOR[] PROGMEM = "i_factor"; const char S_I_MULTIPLIER[] PROGMEM = "i_multiplier"; -const char S_K_FACTOR[] PROGMEM = "k_factor"; const char S_LOGIN[] PROGMEM = "login"; const char S_LOG_LEVEL[] PROGMEM = "logLevel"; const char S_LOW_TEMP[] PROGMEM = "lowTemp"; @@ -143,7 +143,6 @@ const char S_NAME[] PROGMEM = "name"; const char S_NATIVE_HEATING_CONTROL[] PROGMEM = "nativeHeatingControl"; const char S_NETWORK[] PROGMEM = "network"; const char S_NTP[] PROGMEM = "ntp"; -const char S_N_FACTOR[] PROGMEM = "n_factor"; const char S_OFFSET[] PROGMEM = "offset"; const char S_ON_ENABLED_HEATING[] PROGMEM = "onEnabledHeating"; const char S_ON_FAULT[] PROGMEM = "onFault"; @@ -182,9 +181,11 @@ const char S_SERIAL[] PROGMEM = "serial"; const char S_SERVER[] PROGMEM = "server"; const char S_SETTINGS[] PROGMEM = "settings"; const char S_SET_DATE_AND_TIME[] PROGMEM = "setDateAndTime"; +const char S_SHIFT[] PROGMEM = "shift"; const char S_SIGNAL_QUALITY[] PROGMEM = "signalQuality"; const char S_SIZE[] PROGMEM = "size"; const char S_SLAVE[] PROGMEM = "slave"; +const char S_SLOPE[] PROGMEM = "slope"; const char S_SSID[] PROGMEM = "ssid"; const char S_STA[] PROGMEM = "sta"; const char S_STATE[] PROGMEM = "state"; @@ -196,6 +197,7 @@ const char S_SUBNET[] PROGMEM = "subnet"; const char S_SUMMER_WINTER_MODE[] PROGMEM = "summerWinterMode"; const char S_SYSTEM[] PROGMEM = "system"; const char S_TARGET[] PROGMEM = "target"; +const char S_TARGET_DIFF_FACTOR[] PROGMEM = "targetDiffFactor"; const char S_TARGET_TEMP[] PROGMEM = "targetTemp"; const char S_TELNET[] PROGMEM = "telnet"; const char S_TEMPERATURE[] PROGMEM = "temperature"; @@ -208,7 +210,6 @@ const char S_TRESHOLD_TIME[] PROGMEM = "tresholdTime"; const char S_TURBO[] PROGMEM = "turbo"; const char S_TURBO_FACTOR[] PROGMEM = "turboFactor"; const char S_TYPE[] PROGMEM = "type"; -const char S_T_FACTOR[] PROGMEM = "t_factor"; const char S_UNIT_SYSTEM[] PROGMEM = "unitSystem"; const char S_UPTIME[] PROGMEM = "uptime"; const char S_USE[] PROGMEM = "use"; diff --git a/src/utils.h b/src/utils.h index 8442dbf..2c7b64a 100644 --- a/src/utils.h +++ b/src/utils.h @@ -517,9 +517,10 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) { auto equitherm = dst[FPSTR(S_EQUITHERM)].to(); equitherm[FPSTR(S_ENABLED)] = src.equitherm.enabled; - equitherm[FPSTR(S_N_FACTOR)] = roundf(src.equitherm.n_factor, 3); - equitherm[FPSTR(S_K_FACTOR)] = roundf(src.equitherm.k_factor, 3); - equitherm[FPSTR(S_T_FACTOR)] = roundf(src.equitherm.t_factor, 3); + equitherm[FPSTR(S_SLOPE)] = roundf(src.equitherm.slope, 3); + equitherm[FPSTR(S_EXPONENT)] = roundf(src.equitherm.exponent, 3); + equitherm[FPSTR(S_SHIFT)] = roundf(src.equitherm.shift, 2); + equitherm[FPSTR(S_TARGET_DIFF_FACTOR)] = roundf(src.equitherm.targetDiffFactor, 3); auto pid = dst[FPSTR(S_PID)].to(); pid[FPSTR(S_ENABLED)] = src.pid.enabled; @@ -1127,29 +1128,38 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false } } - if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_N_FACTOR)].isNull()) { - float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_N_FACTOR)].as(); + if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_SLOPE)].isNull()) { + float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_SLOPE)].as(); - if (value > 0 && value <= 10 && fabsf(value - dst.equitherm.n_factor) > 0.0001f) { - dst.equitherm.n_factor = roundf(value, 3); + if (value > 0.0f && value <= 10.0f && fabsf(value - dst.equitherm.slope) > 0.0001f) { + dst.equitherm.slope = roundf(value, 3); changed = true; } } - if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_K_FACTOR)].isNull()) { - float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_K_FACTOR)].as(); + if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_EXPONENT)].isNull()) { + float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_EXPONENT)].as(); - if (value >= 0 && value <= 10 && fabsf(value - dst.equitherm.k_factor) > 0.0001f) { - dst.equitherm.k_factor = roundf(value, 3); + if (value > 0.0f && value <= 2.0f && fabsf(value - dst.equitherm.exponent) > 0.0001f) { + dst.equitherm.exponent = roundf(value, 3); changed = true; } } - if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_T_FACTOR)].isNull()) { - float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_T_FACTOR)].as(); + if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_SHIFT)].isNull()) { + float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_SHIFT)].as(); - if (value >= 0 && value <= 10 && fabsf(value - dst.equitherm.t_factor) > 0.0001f) { - dst.equitherm.t_factor = roundf(value, 3); + if (value >= -15.0f && value <= 15.0f && fabsf(value - dst.equitherm.shift) > 0.0001f) { + dst.equitherm.shift = roundf(value, 2); + changed = true; + } + } + + if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_TARGET_DIFF_FACTOR)].isNull()) { + float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_TARGET_DIFF_FACTOR)].as(); + + if (value >= 0.0f && value <= 10.0f && fabsf(value - dst.equitherm.targetDiffFactor) > 0.0001f) { + dst.equitherm.targetDiffFactor = roundf(value, 3); changed = true; } } diff --git a/src_data/locales/en.json b/src_data/locales/en.json index 24ac153..f460b79 100644 --- a/src_data/locales/en.json +++ b/src_data/locales/en.json @@ -371,11 +371,26 @@ }, "equitherm": { - "n": "N factor", - "k": "K factor", - "t": { + "slope": { + "title": "Slope", + "note": "Heat loss compensation. Main tuning parameter." + }, + "exponent": { + "title": "Exponent", + "note": "Radiator efficiency. Typical values: 1.1 - Floor heating, 1.2 - Cast iron, 1.3 - Panel radiators, 1.4 - Convectors." + }, + "shift": { + "title": "Shift", + "note": "Compensates for additional heat losses (e.g., in pipes) or extra heat sources." + }, + "targetDiffFactor": { "title": "T factor", - "note": "Not used if PID is enabled" + "note": "Not used if PID is enabled. Adds to the setpoint the difference between the target and current indoor temp: setpoint = setpoint + ((target - indoor) * T)." + }, + "chart": { + "targetTemp": "Target indoor temperature", + "setpointTemp": "Heat carrier temperature", + "outdoorTemp": "Outdoor temperature" } }, diff --git a/src_data/locales/it.json b/src_data/locales/it.json index 2e4f15c..a9a0227 100644 --- a/src_data/locales/it.json +++ b/src_data/locales/it.json @@ -371,11 +371,26 @@ }, "equitherm": { - "n": "Fattore N", - "k": "Fattore K", - "t": { + "slope": { + "title": "Pendenza", + "note": "Compensazione delle perdite di calore. Principale parametro di regolazione." + }, + "exponent": { + "title": "Esponente", + "note": "Efficienza del radiatore. Valori tipici: 1.1 - Riscaldamento a pavimento, 1.2 - Radiatori in ghisa, 1.3 - Radiatori a pannelli, 1.4 - Convetttori." + }, + "shift": { + "title": "Spostare", + "note": "Compensa le perdite di calore aggiuntive (ad esempio, nelle tubature) o fonti di calore extra." + }, + "targetDiffFactor": { "title": "Fattore T", - "note": "Non usato se PID è attivato" + "note": "Non usato se PID è attivato. Aggiunge al setpoint la differenza tra la temperatura interna target e quella attuale: setpoint = setpoint + ((target - indoor) * T)." + }, + "chart": { + "targetTemp": "Temperatura interna target", + "setpointTemp": "Temperatura del portatore di calore", + "outdoorTemp": "Temperatura esterna" } }, diff --git a/src_data/locales/ru.json b/src_data/locales/ru.json index 7a6e627..09b1b4d 100644 --- a/src_data/locales/ru.json +++ b/src_data/locales/ru.json @@ -371,11 +371,26 @@ }, "equitherm": { - "n": "Коэффициент N", - "k": "Коэффициент K", - "t": { + "slope": { + "title": "Наклон", + "note": "Компенсация теплопотерь. Основной параметр настройки." + }, + "exponent": { + "title": "Экспонента", + "note": "Эффективность радиатора. Типичные значения: 1.1 - Тёплый пол, 1.2 - Чугунные радиаторы, 1.3 - Панельные радиаторы, 1.4 - Конвекторы." + }, + "shift": { + "title": "Смещение", + "note": "Компенсирует дополнительные теплопотери (например, в трубах) или дополнительные источники тепла." + }, + "targetDiffFactor": { "title": "Коэффициент T", - "note": "Не используется, если ПИД включен" + "note": "Не используется, если ПИД включен. Добавляет разницу между целевой и текущей температурой в помещении: setpoint = setpoint + ((target - indoor) * T)." + }, + "chart": { + "targetTemp": "Целевая внутренняя температура", + "setpointTemp": "Температура теплоносителя", + "outdoorTemp": "Наружная температура" } }, diff --git a/src_data/pages/sensors.html b/src_data/pages/sensors.html index c36dbb7..b9496f6 100644 --- a/src_data/pages/sensors.html +++ b/src_data/pages/sensors.html @@ -241,7 +241,9 @@ setCheckboxValue("[name='filtering']", data.filtering, sensorForm); setInputValue("[name='filteringFactor']", data.filteringFactor, {}, sensorForm); - sensorForm.querySelector("[name='type']").dispatchEvent(new Event("change")); + setTimeout(() => { + sensorForm.querySelector("[name='type']").dispatchEvent(new Event("change")); + }, 10); setBusy(".form-busy", "form", false, sensorNode); }; diff --git a/src_data/pages/settings.html b/src_data/pages/settings.html index f5c4494..04f3dbc 100644 --- a/src_data/pages/settings.html +++ b/src_data/pages/settings.html @@ -106,7 +106,7 @@ - +
@@ -354,21 +354,44 @@
+
+
+ +
+ + +
+
+
+ +
+
@@ -424,7 +447,7 @@ settings.temp.min - + - + - + - +