feat: many features

* added dashboard on portal
* added settings for serial port and telnet
* added on/off settings for mqtt
* added event selection for emergency mode
* refactor html & css
This commit is contained in:
Yurii
2024-04-11 03:06:56 +03:00
parent 9a29819d4f
commit 3dec390cce
16 changed files with 1479 additions and 649 deletions

View File

@@ -723,8 +723,8 @@ public:
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
doc[FPSTR(HA_MIN)] = 1;
doc[FPSTR(HA_MAX)] = 100;
doc[FPSTR(HA_MIN)] = 1;
doc[FPSTR(HA_MAX)] = 100;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);

View File

@@ -89,16 +89,31 @@ protected:
Log.sinfoln(FPSTR(L_MAIN), F("Restart signal received. Restart after 10 sec."));
}
if (network->isConnected()) {
vars.sensors.rssi = WiFi.RSSI();
vars.states.mqtt = tMqtt->isConnected();
vars.sensors.rssi = network->isConnected() ? WiFi.RSSI() : 0;
if (vars.states.emergency && !settings.emergency.enable) {
vars.states.emergency = false;
}
if (network->isConnected()) {
if (!this->telnetStarted && telnetStream != nullptr) {
telnetStream->begin(23, false);
this->telnetStarted = true;
}
if (!tMqtt->isEnabled() && strlen(settings.mqtt.server) > 0) {
if (settings.mqtt.enable && !tMqtt->isEnabled()) {
tMqtt->enable();
} else if (!settings.mqtt.enable && tMqtt->isEnabled()) {
tMqtt->disable();
}
if (!vars.states.emergency && settings.emergency.enable && settings.emergency.onMqttFault && !tMqtt->isEnabled()) {
vars.states.emergency = true;
} else if (vars.states.emergency && !settings.emergency.onMqttFault) {
vars.states.emergency = false;
}
if (this->firstFailConnect != 0) {
@@ -122,7 +137,7 @@ protected:
tMqtt->disable();
}
if (settings.emergency.enable && !vars.states.emergency) {
if (!vars.states.emergency && settings.emergency.enable && settings.emergency.onNetworkFault) {
if (this->firstFailConnect == 0) {
this->firstFailConnect = millis();
}

View File

@@ -20,6 +20,11 @@ public:
if (this->client != nullptr) {
if (this->client->connected()) {
this->client->stop();
if (this->connected) {
this->onDisconnect();
this->connected = false;
}
}
delete this->client;
@@ -31,6 +36,12 @@ public:
void disable() {
this->client->stop();
if (this->connected) {
this->onDisconnect();
this->connected = false;
}
Task::disable();
Log.sinfoln(FPSTR(L_MQTT), F("Disabled"));
@@ -176,13 +187,15 @@ protected:
this->onConnect();
}
if (!this->connected && settings.emergency.enable && !vars.states.emergency && millis() - this->disconnectedTime > EMERGENCY_TIME_TRESHOLD) {
vars.states.emergency = true;
Log.sinfoln(FPSTR(L_MQTT), F("Emergency mode enabled"));
if (settings.emergency.enable && settings.emergency.onMqttFault) {
if (!this->connected && !vars.states.emergency && millis() - this->disconnectedTime > EMERGENCY_TIME_TRESHOLD) {
vars.states.emergency = true;
Log.sinfoln(FPSTR(L_MQTT), F("Emergency mode enabled"));
} else if (this->connected && vars.states.emergency && millis() - this->connectedTime > 10000) {
vars.states.emergency = false;
Log.sinfoln(FPSTR(L_MQTT), F("Emergency mode disabled"));
} else if (this->connected && vars.states.emergency && millis() - this->connectedTime > 10000) {
vars.states.emergency = false;
Log.sinfoln(FPSTR(L_MQTT), F("Emergency mode disabled"));
}
}
if (!this->connected) {

View File

@@ -86,9 +86,21 @@ protected:
this->webServer->addHandler(indexPage);*/
this->webServer->addHandler(new StaticPage("/", &LittleFS, "/index.html", PORTAL_CACHE));
// dashboard page
auto dashboardPage = (new StaticPage("/dashboard.html", &LittleFS, "/dashboard.html", PORTAL_CACHE))
->setBeforeSendCallback([this]() {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->requestAuthentication(DIGEST_AUTH);
return false;
}
return true;
});
this->webServer->addHandler(dashboardPage);
// restart
this->webServer->on("/restart.html", HTTP_GET, [this]() {
if (this->isNeedAuth()) {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->send(401);
return;
@@ -103,7 +115,7 @@ protected:
// network settings page
auto networkPage = (new StaticPage("/network.html", &LittleFS, "/network.html", PORTAL_CACHE))
->setBeforeSendCallback([this]() {
if (this->isNeedAuth() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->requestAuthentication(DIGEST_AUTH);
return false;
}
@@ -115,7 +127,7 @@ protected:
// settings page
auto settingsPage = (new StaticPage("/settings.html", &LittleFS, "/settings.html", PORTAL_CACHE))
->setBeforeSendCallback([this]() {
if (this->isNeedAuth() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->requestAuthentication(DIGEST_AUTH);
return false;
}
@@ -127,7 +139,7 @@ protected:
// upgrade page
auto upgradePage = (new StaticPage("/upgrade.html", &LittleFS, "/upgrade.html", PORTAL_CACHE))
->setBeforeSendCallback([this]() {
if (this->isNeedAuth() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->requestAuthentication(DIGEST_AUTH);
return false;
}
@@ -138,7 +150,7 @@ protected:
// OTA
auto upgradeHandler = (new UpgradeHandler("/api/upgrade"))->setCanUploadCallback([this](const String& uri) {
if (this->isNeedAuth() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->sendHeader("Connection", "close");
this->webServer->send(401);
return false;
@@ -172,7 +184,7 @@ protected:
// backup
this->webServer->on("/api/backup/save", HTTP_GET, [this]() {
if (this->isNeedAuth()) {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
@@ -196,7 +208,7 @@ protected:
});
this->webServer->on("/api/backup/restore", HTTP_POST, [this]() {
if (this->isNeedAuth()) {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
@@ -253,7 +265,7 @@ protected:
// network
this->webServer->on("/api/network/settings", HTTP_GET, [this]() {
if (this->isNeedAuth()) {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
@@ -267,7 +279,7 @@ protected:
});
this->webServer->on("/api/network/settings", HTTP_POST, [this]() {
if (this->isNeedAuth()) {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
@@ -298,8 +310,14 @@ protected:
doc.clear();
doc.shrinkToFit();
networkSettingsToJson(networkSettings, doc);
doc.shrinkToFit();
this->bufferedWebServer->send(changed ? 201 : 200, "application/json", doc);
if (changed) {
this->webServer->send(201);
doc.clear();
doc.shrinkToFit();
fsNetworkSettings.update();
network->setHostname(networkSettings.hostname)
@@ -312,33 +330,11 @@ protected:
networkSettings.staticConfig.dns
)
->reconnect();
} else {
this->webServer->send(200);
}
});
this->webServer->on("/api/network/status", HTTP_GET, [this]() {
bool isConnected = network->isConnected();
JsonDocument doc;
doc["hostname"] = networkSettings.hostname;
doc["mac"] = network->getStaMac();
doc["isConnected"] = isConnected;
doc["ssid"] = network->getStaSsid();
doc["signalQuality"] = isConnected ? Network::Manager::rssiToSignalQuality(network->getRssi()) : 0;
doc["channel"] = isConnected ? network->getStaChannel() : 0;
doc["ip"] = isConnected ? network->getStaIp().toString() : "";
doc["subnet"] = isConnected ? network->getStaSubnet().toString() : "";
doc["gateway"] = isConnected ? network->getStaGateway().toString() : "";
doc["dns"] = isConnected ? network->getStaDns().toString() : "";
doc.shrinkToFit();
this->bufferedWebServer->send(200, "application/json", doc);
});
this->webServer->on("/api/network/scan", HTTP_GET, [this]() {
if (this->isNeedAuth()) {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->send(401);
return;
@@ -374,7 +370,7 @@ protected:
// settings
this->webServer->on("/api/settings", HTTP_GET, [this]() {
if (this->isNeedAuth()) {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
@@ -388,7 +384,7 @@ protected:
});
this->webServer->on("/api/settings", HTTP_POST, [this]() {
if (this->isNeedAuth()) {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
@@ -419,12 +415,15 @@ protected:
doc.clear();
doc.shrinkToFit();
if (changed) {
fsSettings.update();
this->webServer->send(201);
settingsToJson(settings, doc);
doc.shrinkToFit();
} else {
this->webServer->send(200);
this->bufferedWebServer->send(changed ? 201 : 200, "application/json", doc);
if (changed) {
doc.clear();
doc.shrinkToFit();
fsSettings.update();
}
});
@@ -433,25 +432,13 @@ protected:
this->webServer->on("/api/vars", HTTP_GET, [this]() {
JsonDocument doc;
varsToJson(vars, doc);
doc["system"]["unitSystem"] = static_cast<byte>(settings.system.unitSystem);
doc["system"]["version"] = PROJECT_VERSION;
doc["system"]["buildDate"] = __DATE__ " " __TIME__;
doc["system"]["uptime"] = millis() / 1000ul;
doc["system"]["totalHeap"] = getTotalHeap();
doc["system"]["freeHeap"] = getFreeHeap();
doc["system"]["minFreeHeap"] = getFreeHeap(true);
doc["system"]["maxFreeBlockHeap"] = getMaxFreeBlockHeap();
doc["system"]["minMaxFreeBlockHeap"] = getMaxFreeBlockHeap(true);
doc["system"]["resetReason"] = getResetReason();
doc["system"]["mqttConnected"] = tMqtt->isConnected();
doc.shrinkToFit();
this->bufferedWebServer->send(200, "application/json", doc);
});
this->webServer->on("/api/vars", HTTP_POST, [this]() {
if (this->isNeedAuth()) {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
@@ -482,12 +469,39 @@ protected:
doc.clear();
doc.shrinkToFit();
if (changed) {
this->webServer->send(201);
varsToJson(vars, doc);
doc.shrinkToFit();
this->bufferedWebServer->send(changed ? 201 : 200, "application/json", doc);
});
} else {
this->webServer->send(200);
}
this->webServer->on("/api/info", HTTP_GET, [this]() {
bool isConnected = network->isConnected();
JsonDocument doc;
doc["network"]["hostname"] = networkSettings.hostname;
doc["network"]["mac"] = network->getStaMac();
doc["network"]["connected"] = isConnected;
doc["network"]["ssid"] = network->getStaSsid();
doc["network"]["signalQuality"] = isConnected ? Network::Manager::rssiToSignalQuality(network->getRssi()) : 0;
doc["network"]["channel"] = isConnected ? network->getStaChannel() : 0;
doc["network"]["ip"] = isConnected ? network->getStaIp().toString() : "";
doc["network"]["subnet"] = isConnected ? network->getStaSubnet().toString() : "";
doc["network"]["gateway"] = isConnected ? network->getStaGateway().toString() : "";
doc["network"]["dns"] = isConnected ? network->getStaDns().toString() : "";
doc["system"]["version"] = PROJECT_VERSION;
doc["system"]["buildDate"] = __DATE__ " " __TIME__;
doc["system"]["uptime"] = millis() / 1000ul;
doc["system"]["totalHeap"] = getTotalHeap();
doc["system"]["freeHeap"] = getFreeHeap();
doc["system"]["minFreeHeap"] = getFreeHeap(true);
doc["system"]["maxFreeBlockHeap"] = getMaxFreeBlockHeap();
doc["system"]["minMaxFreeBlockHeap"] = getMaxFreeBlockHeap(true);
doc["system"]["resetReason"] = getResetReason();
doc.shrinkToFit();
this->bufferedWebServer->send(200, "application/json", doc);
});
@@ -560,8 +574,8 @@ protected:
}
}
bool isNeedAuth() {
return !network->isApEnabled() && settings.portal.useAuth && strlen(settings.portal.password);
bool isAuthRequired() {
return !network->isApEnabled() && settings.portal.auth && strlen(settings.portal.password);
}
void onCaptivePortal() {

View File

@@ -25,13 +25,22 @@ struct NetworkSettings {
struct Settings {
struct {
bool debug = DEBUG_BY_DEFAULT;
bool useSerial = USE_SERIAL;
bool useTelnet = USE_TELNET;
struct {
bool enable = USE_SERIAL;
unsigned int baudrate = 115200;
} serial;
struct {
bool enable = USE_TELNET;
unsigned short port = 23;
} telnet;
UnitSystem unitSystem = UnitSystem::METRIC;
} system;
struct {
bool useAuth = false;
bool auth = false;
char login[13] = DEFAULT_PORTAL_LOGIN;
char password[33] = DEFAULT_PORTAL_PASSWORD;
} portal;
@@ -52,6 +61,7 @@ struct Settings {
} opentherm;
struct {
bool enable = false;
char server[81] = DEFAULT_MQTT_SERVER;
unsigned short port = DEFAULT_MQTT_PORT;
char user[33] = DEFAULT_MQTT_USER;
@@ -65,6 +75,8 @@ struct Settings {
float target = 40.0f;
bool useEquitherm = false;
bool usePid = false;
bool onNetworkFault = true;
bool onMqttFault = true;
} emergency;
struct {
@@ -142,6 +154,7 @@ struct Variables {
bool fault = false;
bool diagnostic = false;
bool externalPump = false;
bool mqtt = false;
} states;
struct {

View File

@@ -59,7 +59,7 @@ void setup() {
return tm{sec, min, hour};
});
Serial.begin(115200);
Serial.begin(settings.system.serial.baudrate);
Log.addStream(&Serial);
Log.print("\n\n\r");
@@ -109,12 +109,12 @@ void setup() {
}
// logs
if (!settings.system.useSerial) {
if (!settings.system.serial.enable) {
Log.clearStreams();
Serial.end();
}
if (settings.system.useTelnet) {
if (settings.system.telnet.enable) {
telnetStream = new ESPTelnetStream;
telnetStream->setKeepAliveInterval(500);
Log.addStream(telnetStream);

View File

@@ -57,16 +57,8 @@ float convertTemp(float value, const UnitSystem unitFrom, const UnitSystem unitT
return value;
}
bool isValidTemp(const float value, UnitSystem unit, const float min = 0.1f, const float max = 99.9f) {
if (unit == UnitSystem::METRIC) {
return value >= min && value <= max;
} else if (unit == UnitSystem::IMPERIAL) {
return value >= c2f(min) && value <= c2f(max);
} else {
return false;
}
bool isValidTemp(const float value, UnitSystem unit, const float min = 0.1f, const float max = 99.9f, const UnitSystem minMaxUnit = UnitSystem::METRIC) {
return value >= convertTemp(min, minMaxUnit, unit) && value <= convertTemp(max, minMaxUnit, unit);
}
double roundd(double value, uint8_t decimals = 2) {
@@ -335,11 +327,13 @@ bool jsonToNetworkSettings(const JsonVariantConst src, NetworkSettings& dst) {
void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
if (!safe) {
dst["system"]["debug"] = src.system.debug;
dst["system"]["useSerial"] = src.system.useSerial;
dst["system"]["useTelnet"] = src.system.useTelnet;
dst["system"]["serial"]["enable"] = src.system.serial.enable;
dst["system"]["serial"]["baudrate"] = src.system.serial.baudrate;
dst["system"]["telnet"]["enable"] = src.system.telnet.enable;
dst["system"]["telnet"]["port"] = src.system.telnet.port;
dst["system"]["unitSystem"] = static_cast<byte>(src.system.unitSystem);
dst["portal"]["useAuth"] = src.portal.useAuth;
dst["portal"]["auth"] = src.portal.auth;
dst["portal"]["login"] = src.portal.login;
dst["portal"]["password"] = src.portal.password;
@@ -356,6 +350,7 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
dst["opentherm"]["modulationSyncWithHeating"] = src.opentherm.modulationSyncWithHeating;
dst["opentherm"]["getMinMaxTemp"] = src.opentherm.getMinMaxTemp;
dst["mqtt"]["enable"] = src.mqtt.enable;
dst["mqtt"]["server"] = src.mqtt.server;
dst["mqtt"]["port"] = src.mqtt.port;
dst["mqtt"]["user"] = src.mqtt.user;
@@ -368,6 +363,8 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
dst["emergency"]["target"] = roundd(src.emergency.target, 2);
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["heating"]["enable"] = src.heating.enable;
dst["heating"]["turbo"] = src.heating.turbo;
@@ -439,16 +436,34 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
changed = true;
}
if (src["system"]["useSerial"].is<bool>()) {
dst.system.useSerial = src["system"]["useSerial"].as<bool>();
if (src["system"]["serial"]["enable"].is<bool>()) {
dst.system.serial.enable = src["system"]["serial"]["enable"].as<bool>();
changed = true;
}
if (src["system"]["useTelnet"].is<bool>()) {
dst.system.useTelnet = src["system"]["useTelnet"].as<bool>();
if (!src["system"]["serial"]["baudrate"].isNull()) {
unsigned int value = src["system"]["serial"]["baudrate"].as<unsigned int>();
if (value == 9600 || value == 19200 || value == 38400 || value == 57600 || value == 74880 || value == 115200) {
dst.system.serial.baudrate = value;
changed = true;
}
}
if (src["system"]["telnet"]["enable"].is<bool>()) {
dst.system.telnet.enable = src["system"]["telnet"]["enable"].as<bool>();
changed = true;
}
if (!src["system"]["telnet"]["port"].isNull()) {
unsigned short value = src["system"]["telnet"]["port"].as<unsigned short>();
if (value > 0 && value <= 65535) {
dst.system.telnet.port = value;
changed = true;
}
}
if (!src["system"]["unitSystem"].isNull()) {
byte value = src["system"]["unitSystem"].as<unsigned char>();
UnitSystem prevUnitSystem = dst.system.unitSystem;
@@ -484,8 +499,8 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
// portal
if (src["portal"]["useAuth"].is<bool>()) {
dst.portal.useAuth = src["portal"]["useAuth"].as<bool>();
if (src["portal"]["auth"].is<bool>()) {
dst.portal.auth = src["portal"]["auth"].as<bool>();
changed = true;
}
@@ -631,6 +646,11 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
// mqtt
if (src["mqtt"]["enable"].is<bool>()) {
dst.mqtt.enable = src["mqtt"]["enable"].as<bool>();
changed = true;
}
if (!src["mqtt"]["server"].isNull()) {
String value = src["mqtt"]["server"].as<String>();
@@ -693,15 +713,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
changed = true;
}
if (!src["emergency"]["target"].isNull()) {
double value = src["emergency"]["target"].as<double>();
if (isValidTemp(value, dst.system.unitSystem)) {
dst.emergency.target = roundd(value, 2);
changed = true;
}
}
if (src["emergency"]["useEquitherm"].is<bool>()) {
if (dst.sensors.outdoor.type != SensorType::MANUAL) {
dst.emergency.useEquitherm = src["emergency"]["useEquitherm"].as<bool>();
@@ -732,93 +743,29 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
changed = true;
}
// heating
if (src["heating"]["enable"].is<bool>()) {
dst.heating.enable = src["heating"]["enable"].as<bool>();
if (src["emergency"]["onNetworkFault"].is<bool>()) {
dst.emergency.onNetworkFault = src["emergency"]["onNetworkFault"].as<bool>();
changed = true;
}
if (src["heating"]["turbo"].is<bool>()) {
dst.heating.turbo = src["heating"]["turbo"].as<bool>();
if (src["emergency"]["onMqttFault"].is<bool>()) {
dst.emergency.onMqttFault = src["emergency"]["onMqttFault"].as<bool>();
changed = true;
}
if (!src["heating"]["target"].isNull()) {
double value = src["heating"]["target"].as<double>();
if (!src["emergency"]["target"].isNull()) {
double value = src["emergency"]["target"].as<double>();
bool noRegulators = (!dst.emergency.useEquitherm && !dst.emergency.usePid);
bool valid = isValidTemp(
value,
dst.system.unitSystem,
noRegulators ? dst.heating.minTemp : 5,
noRegulators ? dst.heating.maxTemp : 30,
noRegulators ? dst.system.unitSystem : UnitSystem::METRIC
);
if (isValidTemp(value, dst.system.unitSystem)) {
dst.heating.target = roundd(value, 2);
changed = true;
}
}
if (!src["heating"]["hysteresis"].isNull()) {
double value = src["heating"]["hysteresis"].as<double>();
if (value >= 0 && value <= 5) {
dst.heating.hysteresis = roundd(value, 2);
changed = true;
}
}
if (!src["heating"]["minTemp"].isNull()) {
unsigned char value = src["heating"]["minTemp"].as<unsigned char>();
if (value >= vars.parameters.heatingMinTemp && value <= vars.parameters.heatingMaxTemp) {
dst.heating.minTemp = value;
changed = true;
}
}
if (!src["heating"]["maxTemp"].isNull()) {
unsigned char value = src["heating"]["maxTemp"].as<unsigned char>();
if (value >= vars.parameters.heatingMinTemp && value <= vars.parameters.heatingMaxTemp) {
dst.heating.maxTemp = value;
changed = true;
}
}
if (!src["heating"]["maxModulation"].isNull()) {
unsigned char value = src["heating"]["maxModulation"].as<unsigned char>();
if (value > 0 && value <= 100) {
dst.heating.maxModulation = value;
changed = true;
}
}
// dhw
if (src["dhw"]["enable"].is<bool>()) {
dst.dhw.enable = src["dhw"]["enable"].as<bool>();
changed = true;
}
if (!src["dhw"]["target"].isNull()) {
unsigned char value = src["dhw"]["target"].as<unsigned char>();
if (isValidTemp(value, dst.system.unitSystem)) {
dst.dhw.target = value;
changed = true;
}
}
if (!src["dhw"]["minTemp"].isNull()) {
unsigned char value = src["dhw"]["minTemp"].as<unsigned char>();
if (value >= vars.parameters.dhwMinTemp && value <= vars.parameters.dhwMaxTemp) {
dst.dhw.minTemp = value;
changed = true;
}
}
if (!src["dhw"]["maxTemp"].isNull()) {
unsigned char value = src["dhw"]["maxTemp"].as<unsigned char>();
if (value >= vars.parameters.dhwMinTemp && value <= vars.parameters.dhwMaxTemp) {
dst.dhw.maxTemp = value;
if (valid) {
dst.emergency.target = roundd(value, 2);
changed = true;
}
}
@@ -919,6 +866,105 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
// heating
if (src["heating"]["enable"].is<bool>()) {
dst.heating.enable = src["heating"]["enable"].as<bool>();
changed = true;
}
if (src["heating"]["turbo"].is<bool>()) {
dst.heating.turbo = src["heating"]["turbo"].as<bool>();
changed = true;
}
if (!src["heating"]["target"].isNull()) {
double value = src["heating"]["target"].as<double>();
bool noRegulators = (!dst.equitherm.enable && !dst.pid.enable);
bool valid = isValidTemp(
value,
dst.system.unitSystem,
noRegulators ? dst.heating.minTemp : 5,
noRegulators ? dst.heating.maxTemp : 30,
noRegulators ? dst.system.unitSystem : UnitSystem::METRIC
);
if (valid) {
dst.heating.target = roundd(value, 2);
changed = true;
}
}
if (!src["heating"]["hysteresis"].isNull()) {
double value = src["heating"]["hysteresis"].as<double>();
if (value >= 0 && value <= 5) {
dst.heating.hysteresis = roundd(value, 2);
changed = true;
}
}
if (!src["heating"]["minTemp"].isNull()) {
unsigned char value = src["heating"]["minTemp"].as<unsigned char>();
if (value >= vars.parameters.heatingMinTemp && value <= vars.parameters.heatingMaxTemp) {
dst.heating.minTemp = value;
changed = true;
}
}
if (!src["heating"]["maxTemp"].isNull()) {
unsigned char value = src["heating"]["maxTemp"].as<unsigned char>();
if (value >= vars.parameters.heatingMinTemp && value <= vars.parameters.heatingMaxTemp) {
dst.heating.maxTemp = value;
changed = true;
}
}
if (!src["heating"]["maxModulation"].isNull()) {
unsigned char value = src["heating"]["maxModulation"].as<unsigned char>();
if (value > 0 && value <= 100) {
dst.heating.maxModulation = value;
changed = true;
}
}
// dhw
if (src["dhw"]["enable"].is<bool>()) {
dst.dhw.enable = src["dhw"]["enable"].as<bool>();
changed = true;
}
if (!src["dhw"]["target"].isNull()) {
unsigned char value = src["dhw"]["target"].as<unsigned char>();
if (isValidTemp(value, dst.system.unitSystem, dst.dhw.minTemp, dst.dhw.maxTemp, dst.system.unitSystem)) {
dst.dhw.target = value;
changed = true;
}
}
if (!src["dhw"]["minTemp"].isNull()) {
unsigned char value = src["dhw"]["minTemp"].as<unsigned char>();
if (value >= vars.parameters.dhwMinTemp && value <= vars.parameters.dhwMaxTemp) {
dst.dhw.minTemp = value;
changed = true;
}
}
if (!src["dhw"]["maxTemp"].isNull()) {
unsigned char value = src["dhw"]["maxTemp"].as<unsigned char>();
if (value >= vars.parameters.dhwMinTemp && value <= vars.parameters.dhwMaxTemp) {
dst.dhw.maxTemp = value;
changed = true;
}
}
// sensors
if (!src["sensors"]["outdoor"]["type"].isNull()) {
byte value = src["sensors"]["outdoor"]["type"].as<unsigned char>();
@@ -974,7 +1020,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (!src["sensors"]["indoor"]["type"].isNull()) {
byte value = src["sensors"]["indoor"]["type"].as<unsigned char>();
switch (value) {
case static_cast<byte>(SensorType::BOILER):
dst.sensors.indoor.type = SensorType::BOILER;
@@ -983,7 +1028,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
case static_cast<byte>(SensorType::MANUAL):
dst.sensors.indoor.type = SensorType::MANUAL;
dst.emergency.useEquitherm = false;
dst.emergency.usePid = false;
changed = true;
break;
@@ -1116,6 +1161,7 @@ void varsToJson(const Variables& src, JsonVariant dst) {
dst["states"]["fault"] = src.states.fault;
dst["states"]["diagnostic"] = src.states.diagnostic;
dst["states"]["externalPump"] = src.states.externalPump;
dst["states"]["mqtt"] = src.states.mqtt;
dst["sensors"]["modulation"] = roundd(src.sensors.modulation, 2);
dst["sensors"]["pressure"] = roundd(src.sensors.pressure, 2);