mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-10 18:24:27 +05:00
refactor: refactoring after #144
This commit is contained in:
@@ -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'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -840,19 +840,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->getObjectIdWithPrefix(F("equitherm_n"));
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_slope"));
|
||||
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("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;
|
||||
@@ -860,79 +860,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->getObjectIdWithPrefix(F("equitherm_k"));
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_exponent"));
|
||||
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("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_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);
|
||||
}
|
||||
|
||||
bool publishInputEquithermFactorE(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("equitherm_e"));
|
||||
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("Equitherm Exponent E");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:alpha-e-circle-outline");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.e_factor|float(0)|round(2) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"e_factor\" : {{ value }}}}");
|
||||
doc[FPSTR(HA_MIN)] = 1;
|
||||
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->getObjectIdWithPrefix(F("equitherm_shift"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||
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_e_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->getObjectIdWithPrefix(F("equitherm_t"));
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_target_diff_factor"));
|
||||
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("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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include <Equitherm.h>
|
||||
#include <GyverPID.h>
|
||||
|
||||
Equitherm etRegulator;
|
||||
GyverPID pidRegulator(0, 0, 0);
|
||||
|
||||
|
||||
@@ -146,40 +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.Ke = settings.equitherm.e_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;
|
||||
|
||||
@@ -134,10 +134,10 @@ struct Settings {
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
float n_factor = 0.7f;
|
||||
float k_factor = 3.0f;
|
||||
float t_factor = 2.0f;
|
||||
float e_factor = 1.3f;
|
||||
float slope = 0.7f;
|
||||
float exponent = 1.3f;
|
||||
float shift = 0.0f;
|
||||
float targetDiffFactor = 2.0f;
|
||||
} equitherm;
|
||||
|
||||
struct {
|
||||
|
||||
@@ -73,12 +73,12 @@ const char S_DNS[] PROGMEM = "dns";
|
||||
const char S_DT[] PROGMEM = "dt";
|
||||
const char S_D_FACTOR[] PROGMEM = "d_factor";
|
||||
const char S_D_MULTIPLIER[] PROGMEM = "d_multiplier";
|
||||
const char S_E_FACTOR[] PROGMEM = "e_factor";
|
||||
const char S_EMERGENCY[] PROGMEM = "emergency";
|
||||
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";
|
||||
@@ -112,7 +112,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_MAC[] PROGMEM = "mac";
|
||||
@@ -138,7 +137,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";
|
||||
@@ -174,9 +172,11 @@ const char S_SENSORS[] PROGMEM = "sensors";
|
||||
const char S_SERIAL[] PROGMEM = "serial";
|
||||
const char S_SERVER[] PROGMEM = "server";
|
||||
const char S_SETTINGS[] PROGMEM = "settings";
|
||||
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";
|
||||
@@ -187,6 +187,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";
|
||||
@@ -199,7 +200,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";
|
||||
|
||||
41
src/utils.h
41
src/utils.h
@@ -500,10 +500,10 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
|
||||
|
||||
auto equitherm = dst[FPSTR(S_EQUITHERM)].to<JsonObject>();
|
||||
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_E_FACTOR)] = roundf(src.equitherm.e_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<JsonObject>();
|
||||
pid[FPSTR(S_ENABLED)] = src.pid.enabled;
|
||||
@@ -1083,37 +1083,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<float>();
|
||||
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_SLOPE)].isNull()) {
|
||||
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_SLOPE)].as<float>();
|
||||
|
||||
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<float>();
|
||||
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_EXPONENT)].isNull()) {
|
||||
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_EXPONENT)].as<float>();
|
||||
|
||||
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_E_FACTOR)].isNull()) {
|
||||
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_E_FACTOR)].as<float>();
|
||||
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_SHIFT)].isNull()) {
|
||||
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_SHIFT)].as<float>();
|
||||
|
||||
if (value >= 1 && value <= 2 && fabsf(value - dst.equitherm.e_factor) > 0.0001f) {
|
||||
dst.equitherm.e_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_T_FACTOR)].isNull()) {
|
||||
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_T_FACTOR)].as<float>();
|
||||
|
||||
if (value >= 0 && value <= 10 && fabsf(value - dst.equitherm.t_factor) > 0.0001f) {
|
||||
dst.equitherm.t_factor = roundf(value, 3);
|
||||
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_TARGET_DIFF_FACTOR)].isNull()) {
|
||||
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_TARGET_DIFF_FACTOR)].as<float>();
|
||||
|
||||
if (value >= 0.0f && value <= 10.0f && fabsf(value - dst.equitherm.targetDiffFactor) > 0.0001f) {
|
||||
dst.equitherm.targetDiffFactor = roundf(value, 3);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,16 +340,26 @@
|
||||
},
|
||||
|
||||
"equitherm": {
|
||||
"n": "N factor",
|
||||
"k": "K factor",
|
||||
"e": "Exponent E",
|
||||
"t": {
|
||||
"slope": {
|
||||
"title": "Slope",
|
||||
"note": ""
|
||||
},
|
||||
"exponent": {
|
||||
"title": "Exponent",
|
||||
"note": ""
|
||||
},
|
||||
"shift": {
|
||||
"title": "Shift",
|
||||
"note": ""
|
||||
},
|
||||
"targetDiffFactor": {
|
||||
"title": "T factor",
|
||||
"note": "Not used if PID is enabled"
|
||||
},
|
||||
"chart": {
|
||||
"radiatorTemp": "Radiator Temperature (°C)",
|
||||
"outdoorTemp": "Outdoor Temperature (°C)"
|
||||
"targetTemp": "Target indoor temperature",
|
||||
"setpointTemp": "Heat carrier temperature",
|
||||
"outdoorTemp": "Outdoor temperature"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -340,16 +340,26 @@
|
||||
},
|
||||
|
||||
"equitherm": {
|
||||
"n": "Fattore N",
|
||||
"k": "Fattore K",
|
||||
"e": "Esponente E",
|
||||
"t": {
|
||||
"slope": {
|
||||
"title": "Pendenza",
|
||||
"note": ""
|
||||
},
|
||||
"exponent": {
|
||||
"title": "Esponente",
|
||||
"note": ""
|
||||
},
|
||||
"shift": {
|
||||
"title": "Spostare",
|
||||
"note": ""
|
||||
},
|
||||
"targetDiffFactor": {
|
||||
"title": "Fattore T",
|
||||
"note": "Non usato se PID è attivato"
|
||||
},
|
||||
"chart": {
|
||||
"radiatorTemp": "Temperatura Del Radiatore (°C)",
|
||||
"outdoorTemp": "Outdoor Temperature (°C)"
|
||||
"targetTemp": "Temperatura interna target",
|
||||
"setpointTemp": "Temperatura del portatore di calore",
|
||||
"outdoorTemp": "Temperatura esterna"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -340,16 +340,26 @@
|
||||
},
|
||||
|
||||
"equitherm": {
|
||||
"n": "Коэффициент N",
|
||||
"k": "Коэффициент K",
|
||||
"e": "Экспонента E",
|
||||
"t": {
|
||||
"slope": {
|
||||
"title": "Наклон",
|
||||
"note": ""
|
||||
},
|
||||
"exponent": {
|
||||
"title": "Экспонента",
|
||||
"note": ""
|
||||
},
|
||||
"shift": {
|
||||
"title": "Смещение",
|
||||
"note": ""
|
||||
},
|
||||
"targetDiffFactor": {
|
||||
"title": "Коэффициент T",
|
||||
"note": "Не используется, если ПИД включен"
|
||||
},
|
||||
"chart": {
|
||||
"radiatorTemp": "Температура радиатора (°C)",
|
||||
"outdoorTemp": "Наружная температура (°C)"
|
||||
"targetTemp": "Целевая внутренняя температура",
|
||||
"setpointTemp": "Температура теплоносителя",
|
||||
"outdoorTemp": "Наружная температура"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -230,7 +230,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);
|
||||
};
|
||||
|
||||
@@ -264,7 +264,6 @@
|
||||
|
||||
<details>
|
||||
<summary><b data-i18n>settings.section.equitherm</b></summary>
|
||||
<canvas id="equithermChart" width="400" height="200"></canvas>
|
||||
<div>
|
||||
<div id="equitherm-settings-busy" aria-busy="true"></div>
|
||||
<form action="/api/settings" id="equitherm-settings" class="hidden">
|
||||
@@ -274,27 +273,45 @@
|
||||
<span data-i18n>settings.enable</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<canvas id="etChart"></canvas>
|
||||
</div>
|
||||
|
||||
<label>
|
||||
<div>
|
||||
<span data-i18n>settings.equitherm.chart.targetTemp</span>: <b class="etChartTargetTempValue"></b>°
|
||||
</div>
|
||||
<input class="etChartTargetTemp" type="range" value="0" min="0" max="0" step="0.5">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>settings.equitherm.n</span>
|
||||
<input type="number" inputmode="decimal" name="equitherm[n_factor]" min="0.001" max="10" step="0.001" required>
|
||||
<span data-i18n>settings.equitherm.slope.title</span>
|
||||
<input type="number" inputmode="decimal" name="equitherm[slope]" min="0.001" max="10" step="0.001" required>
|
||||
<small data-i18n>settings.equitherm.slope.note</small>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.equitherm.k</span>
|
||||
<input type="number" inputmode="decimal" name="equitherm[k_factor]" min="0" max="10" step="0.01" required>
|
||||
<span data-i18n>settings.equitherm.exponent.title</span>
|
||||
<input type="number" inputmode="decimal" name="equitherm[exponent]" min="0.1" max="2" step="0.001" required>
|
||||
<small data-i18n>settings.equitherm.exponent.note</small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>settings.equitherm.shift.title</span>
|
||||
<input type="number" inputmode="decimal" name="equitherm[shift]" min="-15" max="15" step="0.01" required>
|
||||
<small data-i18n>settings.equitherm.shift.note</small>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.equitherm.e</span>
|
||||
<input type="number" inputmode="decimal" name="equitherm[e_factor]" min="1" max="2" step="0.01" required>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.equitherm.t.title</span>
|
||||
<input type="number" inputmode="decimal" name="equitherm[t_factor]" min="0" max="10" step="0.01" required>
|
||||
<small data-i18n>settings.equitherm.t.note</small>
|
||||
<span data-i18n>settings.equitherm.targetDiffFactor.title</span>
|
||||
<input type="number" inputmode="decimal" name="equitherm[targetDiffFactor]" min="0" max="10" step="0.01" required>
|
||||
<small data-i18n>settings.equitherm.targetDiffFactor.note</small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -304,8 +321,6 @@
|
||||
</details>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b data-i18n>settings.section.pid</b></summary>
|
||||
@@ -764,12 +779,144 @@
|
||||
</footer>
|
||||
|
||||
<script src="/static/app.js?{BUILD_TIME}"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="/static/chart.js?{BUILD_TIME}"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const lang = new Lang(document.getElementById('lang'));
|
||||
lang.build();
|
||||
|
||||
let etChart = null;
|
||||
let etChartConfig = {
|
||||
slope: null,
|
||||
exponent: null,
|
||||
shift: null,
|
||||
unitSystem: null,
|
||||
targetTemp: null,
|
||||
minTemp: null,
|
||||
maxTemp: null
|
||||
};
|
||||
|
||||
const makeEquithermChart = () => {
|
||||
if (etChart == null) {
|
||||
const ctx = document.getElementById('etChart').getContext('2d');
|
||||
|
||||
try {
|
||||
etChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [{
|
||||
borderColor: (context) => {
|
||||
const chart = context.chart;
|
||||
const {ctx, chartArea} = chart;
|
||||
|
||||
if (!chartArea) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
|
||||
gradient.addColorStop(0, 'rgba(1, 114, 173, 1)');
|
||||
gradient.addColorStop(0.5, 'rgba(255, 99, 132, 1)');
|
||||
|
||||
return gradient;
|
||||
},
|
||||
borderWidth: 3,
|
||||
fill: false,
|
||||
tension: 0.1,
|
||||
pointRadius: 2,
|
||||
pointHoverRadius: 4,
|
||||
data: []
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
intersect: false
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
position: 'nearest',
|
||||
displayColors: false
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: true,
|
||||
title: {
|
||||
display: true
|
||||
},
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return `${this.getLabelForValue(value)}°`;
|
||||
}
|
||||
}
|
||||
},
|
||||
y: {
|
||||
display: true,
|
||||
title: {
|
||||
display: true
|
||||
},
|
||||
ticks: {
|
||||
callback: (value, index, ticks) => {
|
||||
return `${value}°`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!etChart) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (etChart.data.datasets[0].data.length) {
|
||||
etChart.data.datasets[0].data.pop();
|
||||
}
|
||||
|
||||
for (let value = 30; value >= -30; value--) {
|
||||
const outdoorTemp = etChartConfig.unitSystem == 0 ? value : c2f(value);
|
||||
|
||||
etChart.data.datasets[0].data.push({
|
||||
x: outdoorTemp.toString(),
|
||||
y: calculateEquithermTemp(outdoorTemp)
|
||||
});
|
||||
}
|
||||
|
||||
etChart.data.datasets[0].label = i18n("settings.equitherm.chart.setpointTemp");
|
||||
etChart.options.scales.x.title.text = i18n("settings.equitherm.chart.outdoorTemp");
|
||||
etChart.options.scales.y.title.text = i18n("settings.equitherm.chart.setpointTemp");
|
||||
etChart.update();
|
||||
}
|
||||
|
||||
const calculateEquithermTemp = (outdoorTemp) => {
|
||||
const tempDelta = etChartConfig.targetTemp - outdoorTemp;
|
||||
const maxPoint = etChartConfig.targetTemp - (
|
||||
etChartConfig.maxTemp - etChartConfig.targetTemp
|
||||
) / etChartConfig.slope;
|
||||
|
||||
const sf = (etChartConfig.maxTemp - etChartConfig.targetTemp) / Math.pow(
|
||||
etChartConfig.targetTemp - maxPoint,
|
||||
1 / etChartConfig.exponent
|
||||
);
|
||||
const result = etChartConfig.targetTemp + etChartConfig.shift + sf * (
|
||||
tempDelta >= 0
|
||||
? Math.pow(tempDelta, 1 / etChartConfig.exponent)
|
||||
: -(Math.pow(-(tempDelta), 1 / etChartConfig.exponent))
|
||||
);
|
||||
|
||||
return (Math.max(Math.min(result, etChartConfig.maxTemp), etChartConfig.minTemp)).toFixed(1);
|
||||
}
|
||||
|
||||
const fillData = (data) => {
|
||||
// System
|
||||
setSelectValue("[name='system[logLevel]']", data.system.logLevel);
|
||||
@@ -892,10 +1039,10 @@
|
||||
|
||||
// Equitherm
|
||||
setCheckboxValue("[name='equitherm[enabled]']", data.equitherm.enabled);
|
||||
setInputValue("[name='equitherm[n_factor]']", data.equitherm.n_factor);
|
||||
setInputValue("[name='equitherm[k_factor]']", data.equitherm.k_factor);
|
||||
setInputValue("[name='equitherm[e_factor]']", data.equitherm.e_factor);
|
||||
setInputValue("[name='equitherm[t_factor]']", data.equitherm.t_factor);
|
||||
setInputValue("[name='equitherm[slope]']", data.equitherm.slope);
|
||||
setInputValue("[name='equitherm[exponent]']", data.equitherm.exponent);
|
||||
setInputValue("[name='equitherm[shift]']", data.equitherm.shift);
|
||||
setInputValue("[name='equitherm[targetDiffFactor]']", data.equitherm.targetDiffFactor);
|
||||
setBusy('#equitherm-settings-busy', '#equitherm-settings', false);
|
||||
|
||||
// PID
|
||||
@@ -920,8 +1067,23 @@
|
||||
setInputValue("[name='pid[deadband][thresholdLow]']", data.pid.deadband.thresholdLow);
|
||||
setBusy('#pid-settings-busy', '#pid-settings', false);
|
||||
|
||||
const etMinTemp = parseInt(data.system.unitSystem == 0 ? 15 : 59);
|
||||
const etMaxTemp = parseInt(data.system.unitSystem == 0 ? 30 : 86);
|
||||
const etTargetTemp = constrain(parseFloat(data.heating.target), etMinTemp, etMaxTemp);
|
||||
|
||||
setInputValue(".etChartTargetTemp", etTargetTemp.toFixed(1), {
|
||||
"min": etMinTemp,
|
||||
"max": etMaxTemp
|
||||
});
|
||||
|
||||
etChartConfig.slope = data.equitherm.slope;
|
||||
etChartConfig.exponent = data.equitherm.exponent;
|
||||
etChartConfig.shift = data.equitherm.shift;
|
||||
etChartConfig.unitSystem = data.system.unitSystem;
|
||||
etChartConfig.minTemp = data.heating.minTemp;
|
||||
etChartConfig.maxTemp = data.heating.maxTemp;
|
||||
|
||||
makeEquithermChart();
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -975,189 +1137,52 @@
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
//График
|
||||
let equithermChart;
|
||||
|
||||
|
||||
async function fetchSettings() {
|
||||
try {
|
||||
const response = await fetch("/api/settings", {
|
||||
cache: "no-cache",
|
||||
credentials: "include"
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Response not valid');
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Считаем температуру
|
||||
function calculateTRad(targetTemp, outdoorTemp, maxOut, Kn, Ke, Kk) {
|
||||
let tempDelta = targetTemp - outdoorTemp;
|
||||
const maxPoint = targetTemp - (maxOut - targetTemp) / Kn;
|
||||
let base = targetTemp - maxPoint;
|
||||
|
||||
if (base <= 0) {
|
||||
base = 0.0001;
|
||||
}
|
||||
|
||||
const sf = (maxOut - targetTemp) / Math.pow(base, 1.0 / Ke);
|
||||
|
||||
let T_rad = targetTemp + sf * (tempDelta >= 0 ? Math.pow(tempDelta, 1.0 / Ke) : -Math.pow(-tempDelta, 1.0 / Ke)) + Kk;
|
||||
return Math.min(T_rad, maxOut);
|
||||
}
|
||||
|
||||
// Генерируем данные для графика
|
||||
function generateChartData(targetTemp, maxOut, Kn, Ke, Kk) {
|
||||
const outdoorTemps = [];
|
||||
const predictedTRad = [];
|
||||
|
||||
for (let temp = 25; temp >= -30; temp -= 1) {
|
||||
outdoorTemps.push(temp);
|
||||
predictedTRad.push(calculateTRad(targetTemp, temp, maxOut, Kn, Ke, Kk).toFixed(1));
|
||||
}
|
||||
|
||||
return { outdoorTemps, predictedTRad };
|
||||
}
|
||||
|
||||
// Создаем график
|
||||
function createChart(outdoorTemps, predictedTRad) {
|
||||
const ctx = document.getElementById('equithermChart').getContext('2d');
|
||||
|
||||
const canvasHeight = ctx.canvas.height;
|
||||
const gradient = ctx.createLinearGradient(0, canvasHeight, 0, 0);
|
||||
gradient.addColorStop(0, 'rgba(75, 192, 192, 1)');
|
||||
gradient.addColorStop(0.5, 'rgba(255, 99, 132, 1)');
|
||||
|
||||
equithermChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: outdoorTemps,
|
||||
datasets: [{
|
||||
label: 'Температура Радиатора (°C)',
|
||||
borderColor: gradient,
|
||||
borderWidth: 1,
|
||||
fill: false,
|
||||
tension: 0.1,
|
||||
pointRadius: 2,
|
||||
pointHoverRadius: 4,
|
||||
data: predictedTRad
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
intersect: false
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
position: 'nearest',
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Наружная температура (°C)'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
display: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Температура Радиатора (°C)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Инициализируем график
|
||||
async function initChart() {
|
||||
try {
|
||||
const result = await fetchSettings();
|
||||
|
||||
const { heating, equitherm } = result;
|
||||
const targetTemp = heating?.target ?? 24;
|
||||
const maxOut = heating?.maxTemp ?? 90;
|
||||
const Kn = equitherm?.n_factor ?? 1;
|
||||
const Ke = equitherm?.e_factor ?? 1.3;
|
||||
const Kk = equitherm?.k_factor ?? 0;
|
||||
|
||||
|
||||
const { outdoorTemps, predictedTRad } = generateChartData(targetTemp, maxOut, Kn, Ke, Kk);
|
||||
|
||||
|
||||
createChart(outdoorTemps, predictedTRad);
|
||||
|
||||
|
||||
document.getElementById('equitherm-settings-busy').classList.add('hidden');
|
||||
document.getElementById('equitherm-settings').classList.remove('hidden');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateChart(formData) {
|
||||
if (!equithermChart) return;
|
||||
|
||||
fetchSettings()
|
||||
.then(result => {
|
||||
const targetTemp = result?.heating?.target ?? 24;
|
||||
const maxOut = result?.heating?.maxTemp ?? 90;
|
||||
|
||||
const Kn = parseFloat(formData.get('equitherm[n_factor]')) || 1;
|
||||
const Ke = parseFloat(formData.get('equitherm[e_factor]')) || 1.3;
|
||||
const Kk = parseFloat(formData.get('equitherm[k_factor]')) || 0;
|
||||
|
||||
const { outdoorTemps, predictedTRad } = generateChartData(targetTemp, maxOut, Kn, Ke, Kk);
|
||||
|
||||
equithermChart.data.labels = outdoorTemps;
|
||||
equithermChart.data.datasets[0].data = predictedTRad;
|
||||
equithermChart.update();
|
||||
})
|
||||
.catch(error => console.log(error));
|
||||
}
|
||||
|
||||
|
||||
// Слушаем отправку
|
||||
const form = document.getElementById('equitherm-settings');
|
||||
form.addEventListener('submit', (e) => {
|
||||
|
||||
const formData = new FormData(form);
|
||||
updateChart(formData);
|
||||
document.querySelector(".etChartTargetTemp").addEventListener("input", async (event) => {
|
||||
setValue('.etChartTargetTempValue', parseFloat(event.target.value).toFixed(1));
|
||||
});
|
||||
|
||||
// Слушаем кнопку сохранить
|
||||
const equithermSection = document.querySelector('details');
|
||||
const saveButton = equithermSection.querySelector('button[data-i18n="button.save"]');
|
||||
saveButton.addEventListener('click', () => {
|
||||
const form = document.getElementById('equitherm-settings');
|
||||
const formData = new FormData(form);
|
||||
updateChart(formData);
|
||||
document.querySelector(".etChartTargetTemp").addEventListener("change", async (event) => {
|
||||
if (!event.target.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
etChartConfig.targetTemp = parseFloat(event.target.value);
|
||||
setValue('.etChartTargetTempValue', etChartConfig.targetTemp.toFixed(1));
|
||||
makeEquithermChart();
|
||||
});
|
||||
|
||||
//Следим за изменениями в полях
|
||||
document.querySelectorAll('#equitherm-settings input').forEach(input => {
|
||||
input.addEventListener('change', () => {
|
||||
const form = document.getElementById('equitherm-settings');
|
||||
const formData = new FormData(form);
|
||||
updateChart(formData);
|
||||
});
|
||||
});
|
||||
document.querySelector("[name='equitherm[slope]']").addEventListener("change", async (event) => {
|
||||
if (!event.target.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// инициализируем график
|
||||
initChart();
|
||||
etChartConfig.slope = parseFloat(event.target.value);
|
||||
makeEquithermChart();
|
||||
});
|
||||
|
||||
document.querySelector("[name='equitherm[exponent]']").addEventListener("change", async (event) => {
|
||||
if (!event.target.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
etChartConfig.exponent = parseFloat(event.target.value);
|
||||
makeEquithermChart();
|
||||
});
|
||||
|
||||
document.querySelector("[name='equitherm[shift]']").addEventListener("change", async (event) => {
|
||||
if (!event.target.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
etChartConfig.shift = parseFloat(event.target.value);
|
||||
makeEquithermChart();
|
||||
});
|
||||
|
||||
window.addEventListener('resize', async (event) => {
|
||||
if (etChart) {
|
||||
etChart.resize();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
20
src_data/scripts/chart.js
Normal file
20
src_data/scripts/chart.js
Normal file
File diff suppressed because one or more lines are too long
@@ -630,6 +630,10 @@ const setCheckboxValue = (selector, value, parent = undefined) => {
|
||||
}
|
||||
|
||||
item.checked = value;
|
||||
|
||||
setTimeout(() => {
|
||||
item.dispatchEvent(new Event("change"));
|
||||
}, 10);
|
||||
}
|
||||
|
||||
const setRadioValue = (selector, value, parent = undefined) => {
|
||||
@@ -643,7 +647,14 @@ const setRadioValue = (selector, value, parent = undefined) => {
|
||||
}
|
||||
|
||||
for (let item of items) {
|
||||
item.checked = item.value == value;
|
||||
const checked = item.value == value;
|
||||
if (item.checked != checked) {
|
||||
item.checked = checked;
|
||||
|
||||
setTimeout(() => {
|
||||
item.dispatchEvent(new Event("change"));
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,13 +669,17 @@ const setInputValue = (selector, value, attrs = {}, parent = undefined) => {
|
||||
}
|
||||
|
||||
for (let item of items) {
|
||||
item.value = value;
|
||||
|
||||
if (attrs instanceof Object) {
|
||||
for (let attrKey of Object.keys(attrs)) {
|
||||
item.setAttribute(attrKey, attrs[attrKey]);
|
||||
}
|
||||
}
|
||||
|
||||
item.value = value;
|
||||
|
||||
setTimeout(() => {
|
||||
item.dispatchEvent(new Event("change"));
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,4 +864,16 @@ function dec2hex(i) {
|
||||
}
|
||||
|
||||
return hex.toUpperCase();
|
||||
}
|
||||
|
||||
function c2f(value) {
|
||||
return (9 / 5) * value + 32;
|
||||
}
|
||||
|
||||
function f2c(value) {
|
||||
return (value - 32) * (5 / 9);
|
||||
}
|
||||
|
||||
function constrain(amt, low, high) {
|
||||
return ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)));
|
||||
}
|
||||
Reference in New Issue
Block a user