18 Commits

Author SHA1 Message Date
Yurii
e8d0ad0a4e refactor: improved work with clones (not original) of DS18B20 sensors 2024-12-23 11:37:54 +03:00
Yurii
afe269aeff fix: `Content-Length` for pretty json for chunked response 2024-12-23 11:08:37 +03:00
Yurii
4702909043 fix: BLE power 9 dbm with NimBLE-Arduino 2.1.x 2024-12-18 21:58:45 +03:00
Yurii
4c32ccc450 fix: `Content-Length` on esp32 for chunked response 2024-12-18 20:53:33 +03:00
Yurii
2e3b38e14f refactor: compatibility with NimBLE-Arduino 2.1.x 2024-12-17 11:24:49 +03:00
Yurii
b6c80f355f Merge branch 'master' of https://github.com/Laxilef/OTGateway 2024-12-17 11:22:13 +03:00
github-actions[bot]
65b2a3c2bd chore: bump NimBLE-Arduino from 1.4.3 to 2.1.0
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-17 11:21:04 +03:00
Yurii
0cb361d243 refactor: OT option `heatingCh2Enabled has been renamed to ch2AlwaysEnabled` 2024-12-17 10:30:06 +03:00
Yurii
c7f54ca4fb chore: bump pioarduino/platform-espressif32 from 3.1.0 rc3 to 3.1.0 2024-12-17 09:45:10 +03:00
Yurii
1d46176b5e chore: update pcb 2024-12-17 04:00:29 +03:00
Yurii
5e2f6c9cea refactor: optimization of the web portal layout 2024-12-16 00:43:32 +03:00
Yurii
f439f8c5ba feat: added OT cooling support flag
* refactoring OT settings struct
* renamed some OT settings
2024-12-15 14:24:05 +03:00
Yurii
bae7770371 chore: update pcb 2024-12-07 23:47:21 +03:00
Yurii
4e5a3e9da5 refactor: little changes 2024-12-07 23:10:50 +03:00
Yurii
412e1594e9 chore: update readme & assets 2024-12-07 23:10:27 +03:00
Yurii
9701e8c97b chore: update readme 2024-12-07 22:59:29 +03:00
Yurii
2fe546812c chore: update readme 2024-12-07 22:47:24 +03:00
Yurii
7efcbaa57e chore: bump version to 1.5.0 2024-12-05 00:04:58 +03:00
28 changed files with 15920 additions and 7580 deletions

View File

@@ -1,40 +1,44 @@
<div align="center">
![logo](/assets/logo.svg)
<br>
[![GitHub version](https://img.shields.io/github/release/Laxilef/OTGateway.svg?include_prereleases)](https://github.com/Laxilef/OTGateway/releases)
[![GitHub download](https://img.shields.io/github/downloads/Laxilef/OTGateway/total.svg)](https://github.com/Laxilef/OTGateway/releases/latest)
[![License](https://img.shields.io/github/license/Laxilef/OTGateway.svg)](LICENSE.txt)
[![Telegram](https://img.shields.io/badge/Telegram-Channel-33A8E3)](https://t.me/otgateway)
</div>
<hr />
![Dashboard](/assets/poster-1.png)
![Configuration](/assets/poster-2.png)
![Integration with HomeAssistant](/assets/poster-3.png)
## Features
- Hot water temperature control
- DHW temperature control
- Heating temperature control
- Smart heating temperature control modes:
- PID
- Equithermic curves - adjusts the temperature based on indoor and outdoor temperatures
- Hysteresis setting (for accurate maintenance of room temperature)
- Ability to connect an external sensors to monitor outdoor and indoor temperature ([compatible sensors](https://github.com/Laxilef/OTGateway/wiki/Compatibility#temperature-sensors))
- Emergency mode. If the Wi-Fi connection is lost or the gateway cannot connect to the MQTT server, the mode will turn on. This mode will automatically maintain the set temperature and prevent your home from freezing. In this mode it is also possible to use equithermal curves (weather-compensated control).
- Automatic error reset (not with all boilers)
- Hysteresis setting _(for accurate maintenance of room temperature)_
- Ability to connect [additional (external) sensors](https://github.com/Laxilef/OTGateway/wiki/Compatibility#temperature-sensors): Dallas (1-wire), NTC 10k, Bluetooth (BLE). Makes it possible to monitor indoor and outdoor temperatures, temperatures on pipes/heat exchangers/etc.
- Emergency mode. In any dangerous situation _(loss of connection with Wifi, MQTT, sensors, etc)_ it will not let you and your home freeze.
- Ability of remote fault reset _(not with all boilers)_
- Diagnostics:
- The process of heating: works/does not work
- The process of heating water for hot water: working/not working
- Display of boiler errors
- Burner status (flame): on/off
- Burner modulation level in percent
- Pressure in the heating system
- Gateway status (depending on errors and connection status)
- Boiler connection status via OpenTherm interface
- The current temperature of the heat carrier (usually the return heat carrier)
- Set heat carrier temperature (depending on the selected mode)
- Current hot water temperature
- Displaying gateway status
- Displaying the connection status to the boiler via OpenTherm
- Displaying the fault status and fault code
- Displaying the diagnostic status & diagnostic code
- Display of the process of heating: works/does not work
- Display of burner (flame) status: on/off
- Display of burner modulation level in percent
- Display of pressure in the heating system
- Display of current temperature of the heat carrier
- Display of return temperature of the heat carrier
- Display of setpoint heat carrier temperature (useful when using PID or Equitherm)
- Display of the process of DHW: working/not working
- Display of current DHW temperature
- _And other information..._
- [Home Assistant](https://www.home-assistant.io/) integration via MQTT. The ability to create any automation for the boiler!
![logo](/assets/ha.png)
## Documentation
All available information and instructions can be found in the wiki:
@@ -52,6 +56,7 @@ All available information and instructions can be found in the wiki:
* [Ratios](https://github.com/Laxilef/OTGateway/wiki#ratios)
* [Fit coefficients](https://github.com/Laxilef/OTGateway/wiki#fit-coefficients)
* [PID mode](https://github.com/Laxilef/OTGateway/wiki#pid-mode)
* [Logs and debug](https://github.com/Laxilef/OTGateway/wiki#logs-and-debug)
* [Compatibility](https://github.com/Laxilef/OTGateway/wiki/Compatibility)
* [Boilers](https://github.com/Laxilef/OTGateway/wiki/Compatibility#boilers)
* [Boards](https://github.com/Laxilef/OTGateway/wiki/Compatibility#boards)
@@ -64,24 +69,5 @@ All available information and instructions can be found in the wiki:
* [Connection](https://github.com/Laxilef/OTGateway/wiki/OT-adapters#connection)
* [Leds on board](https://github.com/Laxilef/OTGateway/wiki/OT-adapters#leds-on-board)
## Dependencies
- [ESP8266Scheduler](https://github.com/nrwiersma/ESP8266Scheduler) (for ESP8266)
- [ESP32Scheduler](https://github.com/laxilef/ESP32Scheduler) (for ESP32)
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
- [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library)
- [ArduinoMqttClient](https://github.com/arduino-libraries/ArduinoMqttClient)
- [ESPTelnet](https://github.com/LennartHennigs/ESPTelnet)
- [FileData](https://github.com/GyverLibs/FileData)
- [GyverPID](https://github.com/GyverLibs/GyverPID)
- [GyverBlinker](https://github.com/GyverLibs/GyverBlinker)
- [DallasTemperature](https://github.com/milesburton/Arduino-Temperature-Control-Library)
- [TinyLogger](https://github.com/laxilef/TinyLogger)
## Debug
To display DEBUG messages you must enable debug in settings (switch is disabled by default).
You can connect via Telnet to read messages. IP: ESP8266 ip, port: 23
___
This project is tested with BrowserStack.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 KiB

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 684 KiB

After

Width:  |  Height:  |  Size: 849 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

BIN
assets/poster-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
assets/poster-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
assets/poster-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 KiB

View File

@@ -12,16 +12,17 @@ public:
template <class T>
void send(int code, T contentType, const JsonVariantConst content, bool pretty = false) {
auto contentLength = pretty ? measureJsonPretty(content) : measureJson(content);
#ifdef ARDUINO_ARCH_ESP8266
if (!this->webServer->chunkedResponseModeStart(code, contentType)) {
this->webServer->send(505, F("text/html"), F("HTTP1.1 required"));
return;
}
this->webServer->setContentLength(measureJson(content));
this->webServer->setContentLength(contentLength);
#else
this->webServer->setContentLength(CONTENT_LENGTH_UNKNOWN);
this->webServer->sendHeader(F("Content-Length"), String(measureJson(content)));
this->webServer->setContentLength(contentLength);
this->webServer->send(code, contentType, emptyString);
#endif

View File

@@ -85,13 +85,13 @@ board_build.ldscript = eagle.flash.4m1m.ld
;platform_packages =
; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.5
; framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.1/esp32-arduino-libs-idf-release_v5.1-33fbade6.zip
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10/platform-espressif32.zip
platform_packages =
board_build.partitions = esp32_partitions.csv
lib_deps =
${env.lib_deps}
laxilef/ESP32Scheduler@^1.0.1
nimble_lib = h2zero/NimBLE-Arduino@^1.4.2
nimble_lib = h2zero/NimBLE-Arduino@^2.1.0
lib_ignore =
extra_scripts =
post:tools/esp32.py

View File

@@ -229,7 +229,7 @@ protected:
emergencyFlags |= 0b00000010;
}
if (settings.opentherm.nativeHeatingControl) {
if (settings.opentherm.options.nativeHeatingControl) {
emergencyFlags |= 0b00000100;
}
}

View File

@@ -532,13 +532,13 @@ protected:
bool publishNonStaticHaEntities(bool force = false) {
static byte _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp = 0;
static bool _indoorTempControl, _dhwPresent = false;
static bool _indoorTempControl, _dhwSupport = false;
bool published = false;
if (force || _dhwPresent != settings.opentherm.dhwPresent) {
_dhwPresent = settings.opentherm.dhwPresent;
if (force || _dhwSupport != settings.opentherm.options.dhwSupport) {
_dhwSupport = settings.opentherm.options.dhwSupport;
if (_dhwPresent) {
if (_dhwSupport) {
this->haHelper->publishInputDhwMinTemp(settings.system.unitSystem);
this->haHelper->publishInputDhwMaxTemp(settings.system.unitSystem);
this->haHelper->publishDhwState();
@@ -569,7 +569,7 @@ protected:
published = true;
}
if (_dhwPresent && (force || _dhwMinTemp != settings.dhw.minTemp || _dhwMaxTemp != settings.dhw.maxTemp)) {
if (_dhwSupport && (force || _dhwMinTemp != settings.dhw.minTemp || _dhwMaxTemp != settings.dhw.maxTemp)) {
_dhwMinTemp = settings.dhw.minTemp;
_dhwMaxTemp = settings.dhw.maxTemp;

View File

@@ -153,18 +153,18 @@ protected:
&& !vars.master.heating.blocking;
// DHW settings
vars.master.dhw.enabled = settings.opentherm.dhwPresent && settings.dhw.enabled;
vars.master.dhw.enabled = settings.opentherm.options.dhwSupport && settings.dhw.enabled;
vars.master.dhw.targetTemp = settings.dhw.target;
// CH2 settings
vars.master.ch2.enabled = settings.opentherm.heatingCh2Enabled
|| (settings.opentherm.heatingCh1ToCh2 && vars.master.heating.enabled)
|| (settings.opentherm.dhwToCh2 && settings.opentherm.dhwPresent && settings.dhw.enabled);
vars.master.ch2.enabled = settings.opentherm.options.ch2AlwaysEnabled
|| (settings.opentherm.options.heatingToCh2 && vars.master.heating.enabled)
|| (settings.opentherm.options.dhwToCh2 && settings.opentherm.options.dhwSupport && settings.dhw.enabled);
if (settings.opentherm.heatingCh1ToCh2) {
if (settings.opentherm.options.heatingToCh2) {
vars.master.ch2.targetTemp = vars.master.heating.setpointTemp;
} else if (settings.opentherm.dhwToCh2) {
} else if (settings.opentherm.options.dhwToCh2) {
vars.master.ch2.targetTemp = vars.master.dhw.targetTemp;
}
@@ -174,18 +174,18 @@ protected:
// Immergas fix
// https://arduino.ru/forum/programmirovanie/termostat-opentherm-na-esp8266?page=15#comment-649392
if (settings.opentherm.immergasFix) {
if (settings.opentherm.options.immergasFix) {
statusLb = 0xCA;
}
unsigned long response = this->instance->setBoilerStatus(
vars.master.heating.enabled,
vars.master.dhw.enabled,
false,
settings.opentherm.nativeHeatingControl,
settings.opentherm.options.coolingSupport,
settings.opentherm.options.nativeHeatingControl,
vars.master.ch2.enabled,
settings.opentherm.summerWinterMode,
settings.opentherm.dhwBlocking,
settings.opentherm.options.summerWinterMode,
settings.opentherm.options.dhwBlocking,
statusLb
);
@@ -265,15 +265,16 @@ protected:
}
vars.slave.heating.active = CustomOpenTherm::isCentralHeatingActive(response);
vars.slave.dhw.active = settings.opentherm.dhwPresent ? CustomOpenTherm::isHotWaterActive(response) : false;
vars.slave.dhw.active = settings.opentherm.options.dhwSupport ? CustomOpenTherm::isHotWaterActive(response) : false;
vars.slave.flame = CustomOpenTherm::isFlameOn(response);
vars.slave.cooling = CustomOpenTherm::isCoolingActive(response);
vars.slave.fault.active = CustomOpenTherm::isFault(response);
vars.slave.diag.active = CustomOpenTherm::isDiagnostic(response);
Log.snoticeln(
FPSTR(L_OT), F("Received boiler status. Heating: %hhu; DHW: %hhu; flame: %hhu; fault: %hhu; diag: %hhu"),
FPSTR(L_OT), F("Received boiler status. Heating: %hhu; DHW: %hhu; flame: %hhu; cooling: %hhu; fault: %hhu; diag: %hhu"),
vars.slave.heating.active, vars.slave.dhw.active,
vars.slave.flame, vars.slave.fault.active, vars.slave.diag.active
vars.slave.flame, vars.slave.cooling, vars.slave.fault.active, vars.slave.diag.active
);
// These parameters will be updated every minute
@@ -309,7 +310,7 @@ protected:
Log.swarningln(FPSTR(L_OT), F("Failed receive min modulation and max power"));
}
if (!vars.master.heating.enabled && settings.opentherm.modulationSyncWithHeating) {
if (!vars.master.heating.enabled && settings.opentherm.options.modulationSyncWithHeating) {
if (this->setMaxModulationLevel(0)) {
Log.snoticeln(FPSTR(L_OT), F("Set max modulation: 0% (response: %hhu%%)"), vars.slave.modulation.max);
@@ -334,7 +335,7 @@ protected:
// Get DHW min/max temp (if necessary)
if (settings.opentherm.dhwPresent && settings.opentherm.getMinMaxTemp) {
if (settings.opentherm.options.dhwSupport && settings.opentherm.options.getMinMaxTemp) {
if (this->updateMinMaxDhwTemp()) {
uint8_t convertedMinTemp = convertTemp(
vars.slave.dhw.minTemp,
@@ -380,7 +381,7 @@ protected:
// Get heating min/max temp
if (settings.opentherm.getMinMaxTemp) {
if (settings.opentherm.options.getMinMaxTemp) {
if (this->updateMinMaxHeatingTemp()) {
uint8_t convertedMinTemp = convertTemp(
vars.slave.heating.minTemp,
@@ -520,7 +521,7 @@ protected:
}
// Update DHW temp
if (settings.opentherm.dhwPresent && Sensors::getAmountByType(Sensors::Type::OT_DHW_TEMP, true)) {
if (settings.opentherm.options.dhwSupport && Sensors::getAmountByType(Sensors::Type::OT_DHW_TEMP, true)) {
bool result = this->updateDhwTemp();
if (result) {
@@ -546,7 +547,7 @@ protected:
}
// Update DHW temp 2
if (settings.opentherm.dhwPresent && Sensors::getAmountByType(Sensors::Type::OT_DHW_TEMP2, true)) {
if (settings.opentherm.options.dhwSupport && Sensors::getAmountByType(Sensors::Type::OT_DHW_TEMP2, true)) {
if (this->updateDhwTemp2()) {
float convertedDhwTemp2 = convertTemp(
vars.slave.dhw.currentTemp2,
@@ -570,7 +571,7 @@ protected:
}
// Update DHW flow rate
if (settings.opentherm.dhwPresent && Sensors::getAmountByType(Sensors::Type::OT_DHW_FLOW_RATE, true)) {
if (settings.opentherm.options.dhwSupport && Sensors::getAmountByType(Sensors::Type::OT_DHW_FLOW_RATE, true)) {
if (this->updateDhwFlowRate()) {
float convertedDhwFlowRate = convertVolume(
vars.slave.dhw.flowRate,
@@ -643,7 +644,7 @@ protected:
// Update CH2 temp
if (Sensors::getAmountByType(Sensors::Type::OT_CH2_TEMP, true)) {
if (vars.master.ch2.enabled && !settings.opentherm.nativeHeatingControl) {
if (vars.master.ch2.enabled && !settings.opentherm.options.nativeHeatingControl) {
if (this->updateCh2Temp()) {
float convertedCh2Temp = convertTemp(
vars.slave.ch2.currentTemp,
@@ -942,7 +943,7 @@ protected:
}
// Native heating control
if (settings.opentherm.nativeHeatingControl) {
if (settings.opentherm.options.nativeHeatingControl) {
// Converted current indoor temp
float convertedTemp = convertTemp(vars.master.heating.indoorTemp, settings.system.unitSystem, settings.opentherm.unitSystem);
@@ -958,7 +959,7 @@ protected:
}
// Set current CH2 indoor temp
if (settings.opentherm.heatingCh1ToCh2) {
if (settings.opentherm.options.heatingToCh2) {
if (this->setRoomTempCh2(convertedTemp)) {
Log.sinfoln(
FPSTR(L_OT_HEATING), F("Set current CH2 indoor temp: %.2f (converted: %.2f, response: %.2f)"),
@@ -990,7 +991,7 @@ protected:
}
// Set target CH2 temp
if (settings.opentherm.heatingCh1ToCh2 && this->needSetCh2Temp(convertedTemp)) {
if (settings.opentherm.options.heatingToCh2 && this->needSetCh2Temp(convertedTemp)) {
if (this->setRoomSetpointCh2(convertedTemp)) {
this->ch2SetTempTime = millis();
@@ -1006,7 +1007,7 @@ protected:
}
// Normal heating control
if (!settings.opentherm.nativeHeatingControl && vars.master.heating.enabled) {
if (!settings.opentherm.options.nativeHeatingControl && vars.master.heating.enabled) {
// Converted target heating temp
float convertedTemp = convertTemp(vars.master.heating.setpointTemp, settings.system.unitSystem, settings.opentherm.unitSystem);
@@ -1038,8 +1039,8 @@ protected:
}
// Set CH2 temp
if (!settings.opentherm.nativeHeatingControl && vars.master.ch2.enabled) {
if (settings.opentherm.heatingCh1ToCh2 || settings.opentherm.dhwToCh2) {
if (!settings.opentherm.options.nativeHeatingControl && vars.master.ch2.enabled) {
if (settings.opentherm.options.heatingToCh2 || settings.opentherm.options.dhwToCh2) {
// Converted target CH2 temp
float convertedTemp = convertTemp(
vars.master.ch2.targetTemp,

View File

@@ -39,7 +39,7 @@ protected:
this->indoorSensorsConnected = Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP);
//this->outdoorSensorsConnected = Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP);
if (settings.equitherm.enabled || settings.pid.enabled || settings.opentherm.nativeHeatingControl) {
if (settings.equitherm.enabled || settings.pid.enabled || settings.opentherm.options.nativeHeatingControl) {
vars.master.heating.indoorTempControl = true;
vars.master.heating.minTemp = THERMOSTAT_INDOOR_MIN_TEMP;
vars.master.heating.maxTemp = THERMOSTAT_INDOOR_MAX_TEMP;
@@ -93,7 +93,7 @@ protected:
void hysteresis() {
bool useHyst = false;
if (settings.heating.hysteresis > 0.01f && this->indoorSensorsConnected) {
useHyst = settings.equitherm.enabled || settings.pid.enabled || settings.opentherm.nativeHeatingControl;
useHyst = settings.equitherm.enabled || settings.pid.enabled || settings.opentherm.options.nativeHeatingControl;
}
if (useHyst) {
@@ -110,13 +110,13 @@ protected:
}
inline float getHeatingMinSetpointTemp() {
return settings.opentherm.nativeHeatingControl
return settings.opentherm.options.nativeHeatingControl
? vars.master.heating.minTemp
: settings.heating.minTemp;
}
inline float getHeatingMaxSetpointTemp() {
return settings.opentherm.nativeHeatingControl
return settings.opentherm.options.nativeHeatingControl
? vars.master.heating.maxTemp
: settings.heating.maxTemp;
}
@@ -137,7 +137,7 @@ protected:
if (vars.emergency.state) {
return settings.emergency.target;
} else if (settings.opentherm.nativeHeatingControl) {
} else if (settings.opentherm.options.nativeHeatingControl) {
return settings.heating.target;
} else if (!settings.equitherm.enabled && !settings.pid.enabled) {

View File

@@ -175,16 +175,19 @@ protected:
continue;
}
if (millis() - this->dallasSearchTime[gpio] > this->dallasSearchInterval) {
this->dallasSearchTime[gpio] = millis();
instance.begin();
Log.straceln(
FPSTR(L_SENSORS_DALLAS),
F("GPIO %hhu, devices on bus: %hhu, DS18* devices: %hhu"),
gpio, instance.getDeviceCount(), instance.getDS18Count()
);
if (millis() - this->dallasSearchTime[gpio] < this->dallasSearchInterval) {
continue;
}
this->dallasSearchTime[gpio] = millis();
this->owInstances[gpio].reset();
instance.begin();
Log.straceln(
FPSTR(L_SENSORS_DALLAS),
F("GPIO %hhu, devices on bus: %hhu, DS18* devices: %hhu"),
gpio, instance.getDeviceCount(), instance.getDS18Count()
);
}
}
@@ -274,26 +277,15 @@ protected:
unsigned long ts = millis();
if (this->dallasPolling[gpio]) {
auto minPollingTime = instance.millisToWaitForConversion(12);
auto minPollingTime = instance.millisToWaitForConversion(12) * 2;
unsigned long estimatePollingTime = ts - this->dallasLastPollingTime[gpio];
// check conversion time
// isConversionComplete does not work with chinese clones!
if (estimatePollingTime < minPollingTime) {
continue;
}
// check conversion
bool conversionComplete = instance.isConversionComplete();
if (!conversionComplete) {
if (estimatePollingTime > (minPollingTime * 2)) {
this->dallasPolling[gpio] = false;
Log.swarningln(FPSTR(L_SENSORS_DALLAS), F("GPIO %hhu, timeout receiving data"), gpio);
}
continue;
}
// read sensors data for current instance
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
auto& sSensor = Sensors::settings[sensorId];
@@ -345,28 +337,6 @@ protected:
continue;
}
// check sensors on bus
if (!instance.getDeviceCount()) {
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
auto& sSensor = Sensors::settings[sensorId];
// only target & valid sensors
if (!sSensor.enabled || sSensor.type != Sensors::Type::DALLAS_TEMP || sSensor.purpose == Sensors::Purpose::NOT_CONFIGURED) {
continue;
} else if (sSensor.gpio != gpio || isEmptyAddress(sSensor.address)) {
continue;
}
auto& rSensor = Sensors::results[sensorId];
if (rSensor.signalQuality > 0) {
rSensor.signalQuality--;
}
}
continue;
}
// start polling
instance.setResolution(12);
instance.requestTemperatures();
@@ -380,10 +350,10 @@ protected:
void pollingBleSensors() {
#if USE_BLE
if (!NimBLEDevice::getInitialized() && millis() > 5000) {
if (!NimBLEDevice::isInitialized() && millis() > 5000) {
Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Initialized"));
BLEDevice::init("");
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
NimBLEDevice::setPower(9);
}
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
@@ -445,7 +415,7 @@ protected:
bool connectToBleDevice(const uint8_t sensorId) {
#if USE_BLE
if (!NimBLEDevice::getInitialized()) {
if (!NimBLEDevice::isInitialized()) {
return false;
}
@@ -460,7 +430,7 @@ protected:
sSensor.address[0], sSensor.address[1], sSensor.address[2],
sSensor.address[3], sSensor.address[4], sSensor.address[5]
};
const NimBLEAddress address = NimBLEAddress(addr);
const auto address = NimBLEAddress(addr, 0);
NimBLEClient* pClient = nullptr;
pClient = NimBLEDevice::getClientByPeerAddress(address);
@@ -470,12 +440,13 @@ protected:
}
if (pClient == nullptr) {
if (NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
if (NimBLEDevice::getCreatedClientCount() >= NIMBLE_MAX_CONNECTIONS) {
return false;
}
pClient = NimBLEDevice::createClient();
pClient->setConnectTimeout(5);
pClient->setConnectTimeout(5000);
pClient->setSelfDelete(false, true);
}
if(pClient->isConnected()) {
@@ -492,7 +463,6 @@ protected:
sensorId, sSensor.name, address.toString().c_str()
);
NimBLEDevice::deleteClient(pClient);
return false;
}
@@ -536,7 +506,7 @@ protected:
return;
}
NimBLERemoteService* pService = pChar->getRemoteService();
const NimBLERemoteService* pService = pChar->getRemoteService();
if (pService == nullptr) {
return;
}
@@ -609,7 +579,7 @@ protected:
return;
}
NimBLERemoteService* pService = pChar->getRemoteService();
const NimBLERemoteService* pService = pChar->getRemoteService();
if (pService == nullptr) {
return;
}
@@ -694,7 +664,7 @@ protected:
return;
}
NimBLERemoteService* pService = pChar->getRemoteService();
const NimBLERemoteService* pService = pChar->getRemoteService();
if (pService == nullptr) {
return;
}
@@ -793,7 +763,7 @@ protected:
return;
}
NimBLERemoteService* pService = pChar->getRemoteService();
const NimBLERemoteService* pService = pChar->getRemoteService();
if (pService == nullptr) {
return;
}

View File

@@ -57,16 +57,19 @@ struct Settings {
float minPower = 0.0f;
float maxPower = 0.0f;
bool dhwPresent = true;
bool summerWinterMode = false;
bool heatingCh2Enabled = true;
bool heatingCh1ToCh2 = false;
bool dhwToCh2 = false;
bool dhwBlocking = false;
bool modulationSyncWithHeating = false;
bool getMinMaxTemp = true;
bool nativeHeatingControl = false;
bool immergasFix = false;
struct {
bool dhwSupport = true;
bool coolingSupport = false;
bool summerWinterMode = false;
bool ch2AlwaysEnabled = true;
bool heatingToCh2 = false;
bool dhwToCh2 = false;
bool dhwBlocking = false;
bool modulationSyncWithHeating = false;
bool getMinMaxTemp = true;
bool nativeHeatingControl = false;
bool immergasFix = false;
} options;
} opentherm;
struct {
@@ -287,6 +290,7 @@ struct Variables {
bool connected = false;
bool flame = false;
bool cooling = false;
float pressure = 0.0f;
float heatExchangerTemp = 0.0f;

View File

@@ -50,10 +50,13 @@ const char S_BSSID[] PROGMEM = "bssid";
const char S_BUILD[] PROGMEM = "build";
const char S_CASCADE_CONTROL[] PROGMEM = "cascadeControl";
const char S_CHANNEL[] PROGMEM = "channel";
const char S_CH2_ALWAYS_ENABLED[] PROGMEM = "ch2AlwaysEnabled";
const char S_CHIP[] PROGMEM = "chip";
const char S_CODE[] PROGMEM = "code";
const char S_CONNECTED[] PROGMEM = "connected";
const char S_CONTINUES[] PROGMEM = "continues";
const char S_COOLING[] PROGMEM = "cooling";
const char S_COOLING_SUPPORT[] PROGMEM = "coolingSupport";
const char S_CORE[] PROGMEM = "core";
const char S_CORES[] PROGMEM = "cores";
const char S_CRASH[] PROGMEM = "crash";
@@ -62,7 +65,7 @@ const char S_DATA[] PROGMEM = "data";
const char S_DATE[] PROGMEM = "date";
const char S_DHW[] PROGMEM = "dhw";
const char S_DHW_BLOCKING[] PROGMEM = "dhwBlocking";
const char S_DHW_PRESENT[] PROGMEM = "dhwPresent";
const char S_DHW_SUPPORT[] PROGMEM = "dhwSupport";
const char S_DHW_TO_CH2[] PROGMEM = "dhwToCh2";
const char S_DIAG[] PROGMEM = "diag";
const char S_DNS[] PROGMEM = "dns";
@@ -88,8 +91,7 @@ const char S_GET_MIN_MAX_TEMP[] PROGMEM = "getMinMaxTemp";
const char S_GPIO[] PROGMEM = "gpio";
const char S_HEAP[] PROGMEM = "heap";
const char S_HEATING[] PROGMEM = "heating";
const char S_HEATING_CH1_TO_CH2[] PROGMEM = "heatingCh1ToCh2";
const char S_HEATING_CH2_ENABLED[] PROGMEM = "heatingCh2Enabled";
const char S_HEATING_TO_CH2[] PROGMEM = "heatingToCh2";
const char S_HIDDEN[] PROGMEM = "hidden";
const char S_HOME_ASSISTANT_DISCOVERY[] PROGMEM = "homeAssistantDiscovery";
const char S_HOSTNAME[] PROGMEM = "hostname";
@@ -134,6 +136,7 @@ const char S_ON_ENABLED_HEATING[] PROGMEM = "onEnabledHeating";
const char S_ON_FAULT[] PROGMEM = "onFault";
const char S_ON_LOSS_CONNECTION[] PROGMEM = "onLossConnection";
const char S_OPENTHERM[] PROGMEM = "opentherm";
const char S_OPTIONS[] PROGMEM = "options";
const char S_OUTDOOR_TEMP[] PROGMEM = "outdoorTemp";
const char S_OUT_GPIO[] PROGMEM = "outGpio";
const char S_OUTPUT[] PROGMEM = "output";

View File

@@ -377,16 +377,19 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
opentherm[FPSTR(S_MAX_MODULATION)] = src.opentherm.maxModulation;
opentherm[FPSTR(S_MIN_POWER)] = roundf(src.opentherm.minPower, 2);
opentherm[FPSTR(S_MAX_POWER)] = roundf(src.opentherm.maxPower, 2);
opentherm[FPSTR(S_DHW_PRESENT)] = src.opentherm.dhwPresent;
opentherm[FPSTR(S_SUMMER_WINTER_MODE)] = src.opentherm.summerWinterMode;
opentherm[FPSTR(S_HEATING_CH2_ENABLED)] = src.opentherm.heatingCh2Enabled;
opentherm[FPSTR(S_HEATING_CH1_TO_CH2)] = src.opentherm.heatingCh1ToCh2;
opentherm[FPSTR(S_DHW_TO_CH2)] = src.opentherm.dhwToCh2;
opentherm[FPSTR(S_DHW_BLOCKING)] = src.opentherm.dhwBlocking;
opentherm[FPSTR(S_MODULATION_SYNC_WITH_HEATING)] = src.opentherm.modulationSyncWithHeating;
opentherm[FPSTR(S_GET_MIN_MAX_TEMP)] = src.opentherm.getMinMaxTemp;
opentherm[FPSTR(S_NATIVE_HEATING_CONTROL)] = src.opentherm.nativeHeatingControl;
opentherm[FPSTR(S_IMMERGAS_FIX)] = src.opentherm.immergasFix;
auto otOptions = opentherm[FPSTR(S_OPTIONS)].to<JsonObject>();
otOptions[FPSTR(S_DHW_SUPPORT)] = src.opentherm.options.dhwSupport;
otOptions[FPSTR(S_COOLING_SUPPORT)] = src.opentherm.options.coolingSupport;
otOptions[FPSTR(S_SUMMER_WINTER_MODE)] = src.opentherm.options.summerWinterMode;
otOptions[FPSTR(S_CH2_ALWAYS_ENABLED)] = src.opentherm.options.ch2AlwaysEnabled;
otOptions[FPSTR(S_HEATING_TO_CH2)] = src.opentherm.options.heatingToCh2;
otOptions[FPSTR(S_DHW_TO_CH2)] = src.opentherm.options.dhwToCh2;
otOptions[FPSTR(S_DHW_BLOCKING)] = src.opentherm.options.dhwBlocking;
otOptions[FPSTR(S_MODULATION_SYNC_WITH_HEATING)] = src.opentherm.options.modulationSyncWithHeating;
otOptions[FPSTR(S_GET_MIN_MAX_TEMP)] = src.opentherm.options.getMinMaxTemp;
otOptions[FPSTR(S_NATIVE_HEATING_CONTROL)] = src.opentherm.options.nativeHeatingControl;
otOptions[FPSTR(S_IMMERGAS_FIX)] = src.opentherm.options.immergasFix;
auto mqtt = dst[FPSTR(S_MQTT)].to<JsonObject>();
mqtt[FPSTR(S_ENABLED)] = src.mqtt.enabled;
@@ -718,101 +721,110 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
}
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_DHW_PRESENT)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_DHW_PRESENT)].as<bool>();
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_DHW_SUPPORT)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_DHW_SUPPORT)].as<bool>();
if (value != dst.opentherm.dhwPresent) {
dst.opentherm.dhwPresent = value;
if (value != dst.opentherm.options.dhwSupport) {
dst.opentherm.options.dhwSupport = value;
changed = true;
}
}
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_SUMMER_WINTER_MODE)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_SUMMER_WINTER_MODE)].as<bool>();
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_COOLING_SUPPORT)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_COOLING_SUPPORT)].as<bool>();
if (value != dst.opentherm.summerWinterMode) {
dst.opentherm.summerWinterMode = value;
if (value != dst.opentherm.options.coolingSupport) {
dst.opentherm.options.coolingSupport = value;
changed = true;
}
}
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_HEATING_CH2_ENABLED)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_HEATING_CH2_ENABLED)].as<bool>();
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_SUMMER_WINTER_MODE)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_SUMMER_WINTER_MODE)].as<bool>();
if (value != dst.opentherm.heatingCh2Enabled) {
dst.opentherm.heatingCh2Enabled = value;
if (value != dst.opentherm.options.summerWinterMode) {
dst.opentherm.options.summerWinterMode = value;
changed = true;
}
}
if (dst.opentherm.heatingCh2Enabled) {
dst.opentherm.heatingCh1ToCh2 = false;
dst.opentherm.dhwToCh2 = false;
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_CH2_ALWAYS_ENABLED)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_CH2_ALWAYS_ENABLED)].as<bool>();
if (value != dst.opentherm.options.ch2AlwaysEnabled) {
dst.opentherm.options.ch2AlwaysEnabled = value;
if (dst.opentherm.options.ch2AlwaysEnabled) {
dst.opentherm.options.heatingToCh2 = false;
dst.opentherm.options.dhwToCh2 = false;
}
changed = true;
}
}
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_HEATING_CH1_TO_CH2)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_HEATING_CH1_TO_CH2)].as<bool>();
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_HEATING_TO_CH2)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_HEATING_TO_CH2)].as<bool>();
if (value != dst.opentherm.heatingCh1ToCh2) {
dst.opentherm.heatingCh1ToCh2 = value;
if (value != dst.opentherm.options.heatingToCh2) {
dst.opentherm.options.heatingToCh2 = value;
if (dst.opentherm.heatingCh1ToCh2) {
dst.opentherm.heatingCh2Enabled = false;
dst.opentherm.dhwToCh2 = false;
if (dst.opentherm.options.heatingToCh2) {
dst.opentherm.options.ch2AlwaysEnabled = false;
dst.opentherm.options.dhwToCh2 = false;
}
changed = true;
}
}
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_DHW_TO_CH2)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_DHW_TO_CH2)].as<bool>();
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_DHW_TO_CH2)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_DHW_TO_CH2)].as<bool>();
if (value != dst.opentherm.dhwToCh2) {
dst.opentherm.dhwToCh2 = value;
if (value != dst.opentherm.options.dhwToCh2) {
dst.opentherm.options.dhwToCh2 = value;
if (dst.opentherm.dhwToCh2) {
dst.opentherm.heatingCh2Enabled = false;
dst.opentherm.heatingCh1ToCh2 = false;
if (dst.opentherm.options.dhwToCh2) {
dst.opentherm.options.ch2AlwaysEnabled = false;
dst.opentherm.options.heatingToCh2 = false;
}
changed = true;
}
}
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_DHW_BLOCKING)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_DHW_BLOCKING)].as<bool>();
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_DHW_BLOCKING)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_DHW_BLOCKING)].as<bool>();
if (value != dst.opentherm.dhwBlocking) {
dst.opentherm.dhwBlocking = value;
if (value != dst.opentherm.options.dhwBlocking) {
dst.opentherm.options.dhwBlocking = value;
changed = true;
}
}
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_MODULATION_SYNC_WITH_HEATING)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_MODULATION_SYNC_WITH_HEATING)].as<bool>();
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_MODULATION_SYNC_WITH_HEATING)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_MODULATION_SYNC_WITH_HEATING)].as<bool>();
if (value != dst.opentherm.modulationSyncWithHeating) {
dst.opentherm.modulationSyncWithHeating = value;
if (value != dst.opentherm.options.modulationSyncWithHeating) {
dst.opentherm.options.modulationSyncWithHeating = value;
changed = true;
}
}
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_GET_MIN_MAX_TEMP)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_GET_MIN_MAX_TEMP)].as<bool>();
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_GET_MIN_MAX_TEMP)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_GET_MIN_MAX_TEMP)].as<bool>();
if (value != dst.opentherm.getMinMaxTemp) {
dst.opentherm.getMinMaxTemp = value;
if (value != dst.opentherm.options.getMinMaxTemp) {
dst.opentherm.options.getMinMaxTemp = value;
changed = true;
}
}
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_NATIVE_HEATING_CONTROL)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_NATIVE_HEATING_CONTROL)].as<bool>();
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_NATIVE_HEATING_CONTROL)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_NATIVE_HEATING_CONTROL)].as<bool>();
if (value != dst.opentherm.nativeHeatingControl) {
dst.opentherm.nativeHeatingControl = value;
if (value != dst.opentherm.options.nativeHeatingControl) {
dst.opentherm.options.nativeHeatingControl = value;
if (value) {
dst.equitherm.enabled = false;
@@ -823,11 +835,11 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
}
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_IMMERGAS_FIX)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_IMMERGAS_FIX)].as<bool>();
if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_IMMERGAS_FIX)].is<bool>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_IMMERGAS_FIX)].as<bool>();
if (value != dst.opentherm.immergasFix) {
dst.opentherm.immergasFix = value;
if (value != dst.opentherm.options.immergasFix) {
dst.opentherm.options.immergasFix = value;
changed = true;
}
}
@@ -923,7 +935,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (src[FPSTR(S_EQUITHERM)][FPSTR(S_ENABLED)].is<bool>()) {
bool value = src[FPSTR(S_EQUITHERM)][FPSTR(S_ENABLED)].as<bool>();
if (!dst.opentherm.nativeHeatingControl) {
if (!dst.opentherm.options.nativeHeatingControl) {
if (value != dst.equitherm.enabled) {
dst.equitherm.enabled = value;
changed = true;
@@ -967,7 +979,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (src[FPSTR(S_PID)][FPSTR(S_ENABLED)].is<bool>()) {
bool value = src[FPSTR(S_PID)][FPSTR(S_ENABLED)].as<bool>();
if (!dst.opentherm.nativeHeatingControl) {
if (!dst.opentherm.options.nativeHeatingControl) {
if (value != dst.pid.enabled) {
dst.pid.enabled = value;
changed = true;
@@ -1326,7 +1338,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
// force check emergency target
{
float value = !src[FPSTR(S_EMERGENCY)][FPSTR(S_TARGET)].isNull() ? src[FPSTR(S_EMERGENCY)][FPSTR(S_TARGET)].as<float>() : dst.emergency.target;
bool noRegulators = !dst.opentherm.nativeHeatingControl;
bool noRegulators = !dst.opentherm.options.nativeHeatingControl;
bool valid = isValidTemp(
value,
dst.system.unitSystem,
@@ -1351,7 +1363,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
// force check heating target
{
bool indoorTempControl = dst.equitherm.enabled || dst.pid.enabled || dst.opentherm.nativeHeatingControl;
bool indoorTempControl = dst.equitherm.enabled || dst.pid.enabled || dst.opentherm.options.nativeHeatingControl;
float minTemp = indoorTempControl ? THERMOSTAT_INDOOR_MIN_TEMP : dst.heating.minTemp;
float maxTemp = indoorTempControl ? THERMOSTAT_INDOOR_MAX_TEMP : dst.heating.maxTemp;
@@ -1720,6 +1732,7 @@ void varsToJson(const Variables& src, JsonVariant dst) {
slave[FPSTR(S_PROTOCOL_VERSION)] = src.slave.appVersion;
slave[FPSTR(S_CONNECTED)] = src.slave.connected;
slave[FPSTR(S_FLAME)] = src.slave.flame;
slave[FPSTR(S_COOLING)] = src.slave.cooling;
auto sModulation = slave[FPSTR(S_MODULATION)].to<JsonObject>();
sModulation[FPSTR(S_MIN)] = src.slave.modulation.min;

View File

@@ -97,6 +97,7 @@
"sConnected": "OpenTherm connection",
"sFlame": "Flame",
"sCooling": "Cooling",
"sFaultActive": "Fault",
"sFaultCode": "Faul code",
"sDiagActive": "Diagnostic",
@@ -356,10 +357,11 @@
"options": {
"desc": "Options",
"dhwPresent": "DHW present",
"dhwSupport": "DHW support",
"coolingSupport": "Cooling support",
"summerWinterMode": "Summer/winter mode",
"heatingCh2Enabled": "Heating CH2 always enabled",
"heatingCh1ToCh2": "Duplicate heating CH1 to CH2",
"ch2AlwaysEnabled": "CH2 always enabled",
"heatingToCh2": "Duplicate heating to CH2",
"dhwToCh2": "Duplicate DHW to CH2",
"dhwBlocking": "DHW blocking",
"modulationSyncWithHeating": "Sync modulation with heating",

View File

@@ -97,6 +97,7 @@
"sConnected": "Подключение к OpenTherm",
"sFlame": "Пламя",
"sCooling": "Охлаждение",
"sFaultActive": "Ошибка",
"sFaultCode": "Код ошибки",
"sDiagActive": "Диагностика",
@@ -356,10 +357,11 @@
"options": {
"desc": "Опции",
"dhwPresent": "Контур ГВС",
"dhwSupport": "Поддержка ГВС",
"coolingSupport": "Поддержка охлаждения",
"summerWinterMode": "Летний/зимний режим",
"heatingCh2Enabled": "Канал 2 отопления всегда вкл.",
"heatingCh1ToCh2": "Дублировать параметры отопления канала 1 в канал 2",
"ch2AlwaysEnabled": "Канал 2 всегда вкл.",
"heatingToCh2": "Дублировать параметры отопления в канал 2",
"dhwToCh2": "Дублировать параметры ГВС в канал 2",
"dhwBlocking": "DHW blocking",
"modulationSyncWithHeating": "Синхронизировать модуляцию с отоплением",

View File

@@ -113,6 +113,10 @@
<th scope="row" data-i18n>dashboard.states.sFlame</th>
<td><i class="sFlame"></i></td>
</tr>
<tr>
<th scope="row" data-i18n>dashboard.states.sCooling</th>
<td><i class="sCooling"></i></td>
</tr>
<tr>
@@ -372,8 +376,8 @@
(prevSettings.heating.enabled != newSettings.heating.enabled)
|| (prevSettings.heating.turbo != newSettings.heating.turbo)
|| (prevSettings.heating.target != newSettings.heating.target)
|| (prevSettings.opentherm.dhwPresent && prevSettings.dhw.enabled != newSettings.dhw.enabled)
|| (prevSettings.opentherm.dhwPresent && prevSettings.dhw.target != newSettings.dhw.target)
|| (prevSettings.opentherm.options.dhwSupport && prevSettings.dhw.enabled != newSettings.dhw.enabled)
|| (prevSettings.opentherm.options.dhwSupport && prevSettings.dhw.target != newSettings.dhw.target)
);
if (modified) {
@@ -397,7 +401,7 @@
}
const result = await response.json();
noRegulators = !result.opentherm.nativeHeatingControl && !result.equitherm.enabled && !result.pid.enabled;
noRegulators = !result.opentherm.options.nativeHeatingControl && !result.equitherm.enabled && !result.pid.enabled;
prevSettings = result;
unitSystem = result.system.unitSystem;
newSettings.heating.enabled = result.heating.enabled;
@@ -406,7 +410,7 @@
newSettings.dhw.enabled = result.dhw.enabled;
newSettings.dhw.target = result.dhw.target;
if (result.opentherm.dhwPresent) {
if (result.opentherm.options.dhwSupport) {
show('#thermostat-dhw');
} else {
hide('#thermostat-dhw');
@@ -462,6 +466,7 @@
result.slave.connected ? "green" : "red"
);
setState('.sFlame', result.slave.flame);
setState('.sCooling', result.slave.cooling);
setValue('.sModMin', result.slave.modulation.min);
setValue('.sModMax', result.slave.modulation.max);

View File

@@ -38,37 +38,37 @@
<div id="network-settings-busy" aria-busy="true"></div>
<form action="/api/network/settings" id="network-settings" class="hidden">
<label for="network-hostname">
<label>
<span data-i18n>network.params.hostname</span>
<input type="text" id="network-hostname" name="hostname" maxlength="24" pattern="[A-Za-z0-9]+[A-Za-z0-9\-]+[A-Za-z0-9]+" required>
<input type="text" name="hostname" maxlength="24" pattern="[A-Za-z0-9]+[A-Za-z0-9\-]+[A-Za-z0-9]+" required>
</label>
<label for="network-use-dhcp">
<input type="checkbox" id="network-use-dhcp" name="useDhcp" value="true">
<label>
<input type="checkbox" name="useDhcp" value="true">
<span data-i18n>network.params.dhcp</span>
</label>
<br />
<hr />
<h4 data-i18n>network.section.static</h4>
<label for="network-static-ip">
<label>
<span data-i18n>network.params.ip</span>
<input type="text" id="network-static-ip" name="staticConfig[ip]" value="true" maxlength="16" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" required>
<input type="text" name="staticConfig[ip]" value="true" maxlength="16" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" required>
</label>
<label for="network-static-gateway">
<label>
<span data-i18n>network.params.gateway</span>
<input type="text" id="network-static-gateway" name="staticConfig[gateway]" maxlength="16" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" required>
<input type="text" name="staticConfig[gateway]" maxlength="16" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" required>
</label>
<label for="network-static-subnet">
<label>
<span data-i18n>network.params.subnet</span>
<input type="text" id="network-static-subnet" name="staticConfig[subnet]" maxlength="16" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" required>
<input type="text" name="staticConfig[subnet]" maxlength="16" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" required>
</label>
<label for="network-static-dns">
<label>
<span data-i18n>network.params.dns</span>
<input type="text" id="network-static-dns" name="staticConfig[dns]" maxlength="16" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" required>
<input type="text" name="staticConfig[dns]" maxlength="16" pattern="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" required>
</label>
<button type="submit" data-i18n>button.save</button>
@@ -109,19 +109,19 @@
<div id="sta-settings-busy" aria-busy="true"></div>
<form action="/api/network/settings" id="sta-settings" class="hidden">
<label for="sta-ssid">
<label>
<span data-i18n>network.wifi.ssid</span>
<input type="text" id="sta-ssid" name="sta[ssid]" maxlength="32" required>
<input type="text" name="sta[ssid]" maxlength="32" required>
</label>
<label for="sta-password">
<label>
<span data-i18n>network.wifi.password</span>
<input type="password" id="sta-password" name="sta[password]" maxlength="64" required>
<input type="password" name="sta[password]" maxlength="64" required>
</label>
<label for="sta-channel">
<label>
<span data-i18n>network.wifi.channel</span>
<input type="number" inputmode="numeric" id="sta-channel" name="sta[channel]" min="0" max="12" step="1" required>
<input type="number" inputmode="numeric" name="sta[channel]" min="0" max="12" step="1" required>
<small data-i18n>network.sta.channel.note</small>
</label>
@@ -140,19 +140,19 @@
<div id="ap-settings-busy" aria-busy="true"></div>
<form action="/api/network/settings" id="ap-settings" class="hidden">
<label for="ap-ssid">
<label>
<span data-i18n>network.wifi.ssid</span>
<input type="text" id="ap-ssid" name="ap[ssid]" maxlength="32" required>
<input type="text" name="ap[ssid]" maxlength="32" required>
</label>
<label for="ap-password">
<label>
<span data-i18n>network.wifi.password</span>
<input type="text" id="ap-password" name="ap[password]" maxlength="64" required>
<input type="text" name="ap[password]" maxlength="64" required>
</label>
<label for="ap-channel">
<label>
<span data-i18n>network.wifi.channel</span>
<input type="number" inputmode="numeric" id="ap-channel" name="ap[channel]" min="1" max="12" step="1" required>
<input type="number" inputmode="numeric" name="ap[channel]" min="1" max="12" step="1" required>
</label>
<button type="submit" data-i18n>button.save</button>
@@ -179,22 +179,22 @@
lang.build();
const fillData = (data) => {
setInputValue('#network-hostname', data.hostname);
setCheckboxValue('#network-use-dhcp', data.useDhcp);
setInputValue('#network-static-ip', data.staticConfig.ip);
setInputValue('#network-static-gateway', data.staticConfig.gateway);
setInputValue('#network-static-subnet', data.staticConfig.subnet);
setInputValue('#network-static-dns', data.staticConfig.dns);
setInputValue("[name='hostname']", data.hostname);
setCheckboxValue("[name='useDhcp']", data.useDhcp);
setInputValue("[name='staticConfig[ip]']", data.staticConfig.ip);
setInputValue("[name='staticConfig[gateway]']", data.staticConfig.gateway);
setInputValue("[name='staticConfig[subnet]']", data.staticConfig.subnet);
setInputValue("[name='staticConfig[dns]']", data.staticConfig.dns);
setBusy('#network-settings-busy', '#network-settings', false);
setInputValue('#sta-ssid', data.sta.ssid);
setInputValue('#sta-password', data.sta.password);
setInputValue('#sta-channel', data.sta.channel);
setInputValue("[name='sta[ssid]']", data.sta.ssid);
setInputValue("[name='sta[password]']", data.sta.password);
setInputValue("[name='sta[channel]']", data.sta.channel);
setBusy('#sta-settings-busy', '#sta-settings', false);
setInputValue('#ap-ssid', data.ap.ssid);
setInputValue('#ap-password', data.ap.password);
setInputValue('#ap-channel', data.ap.channel);
setInputValue("[name='ap[ssid]']", data.ap.ssid);
setInputValue("[name='ap[password]']", data.ap.password);
setInputValue("[name='ap[channel]']", data.ap.channel);
setBusy('#ap-settings-busy', '#ap-settings', false);
};

View File

@@ -41,19 +41,19 @@
<div id="portal-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="portal-settings" class="hidden">
<div class="grid">
<label for="portal-login">
<label>
<span data-i18n>settings.portal.login</span>
<input type="text" id="portal-login" name="portal[login]" maxlength="12" required>
<input type="text" name="portal[login]" maxlength="12" required>
</label>
<label for="portal-password">
<label>
<span data-i18n>settings.portal.password</span>
<input type="password" id="portal-password" name="portal[password]" maxlength="32" required>
<input type="password" name="portal[password]" maxlength="32" required>
</label>
</div>
<label for="portal-auth">
<input type="checkbox" id="portal-auth" name="portal[auth]" value="true">
<label>
<input type="checkbox" name="portal[auth]" value="true">
<span data-i18n>settings.portal.auth</span>
</label>
<br />
@@ -74,20 +74,20 @@
<legend data-i18n>settings.system.unit</legend>
<label>
<input type="radio" class="system-unit-system" name="system[unitSystem]" value="0" />
<input type="radio" name="system[unitSystem]" value="0" />
<span data-i18n>settings.system.metric</span>
</label>
<label>
<input type="radio" class="system-unit-system" name="system[unitSystem]" value="1" />
<input type="radio" name="system[unitSystem]" value="1" />
<span data-i18n>settings.system.imperial</span>
</label>
</fieldset>
<fieldset>
<label for="system-status-led-gpio">
<label>
<span data-i18n>settings.system.statusLedGpio</span>
<input type="number" inputmode="numeric" id="system-status-led-gpio" name="system[statusLedGpio]" min="0" max="254" step="1">
<input type="number" inputmode="numeric" name="system[statusLedGpio]" min="0" max="254" step="1">
<small data-i18n>settings.note.blankNotUse</small>
</label>
</fieldset>
@@ -95,19 +95,19 @@
<fieldset>
<legend data-i18n>settings.section.diag</legend>
<label for="system-serial-enable">
<input type="checkbox" id="system-serial-enable" name="system[serial][enabled]" value="true">
<label>
<input type="checkbox" name="system[serial][enabled]" value="true">
<span data-i18n>settings.system.serial.enable</span>
</label>
<label for="system-telnet-enable">
<input type="checkbox" id="system-telnet-enable" name="system[telnet][enabled]" value="true">
<label>
<input type="checkbox" name="system[telnet][enabled]" value="true">
<span data-i18n>settings.system.telnet.enable</span>
</label>
<label for="system-log-level">
<label>
<span data-i18n>settings.system.logLevel</span>
<select id="system-log-level" name="system[logLevel]">
<select name="system[logLevel]">
<option value="0">SILENT</option>
<option value="1">FATAL</option>
<option value="2">ERROR</option>
@@ -120,9 +120,9 @@
</label>
<div class="grid">
<label for="system-serial-baudrate">
<label>
<span data-i18n>settings.system.serial.baud</span>
<select id="system-serial-baudrate" name="system[serial][baudrate]" required>
<select name="system[serial][baudrate]" required>
<option value="9600">9600</option>
<option value="19200">19200</option>
<option value="38400">38400</option>
@@ -132,9 +132,9 @@
</select>
</label>
<label for="system-telnet-port">
<label>
<span data-i18n>settings.system.telnet.port.title</span>
<input type="number" inputmode="numeric" id="system-telnet-port" name="system[telnet][port]" min="1" max="65535" step="1" required>
<input type="number" inputmode="numeric" name="system[telnet][port]" min="1" max="65535" step="1" required>
<small data-i18n>settings.system.telnet.port.note</small>
</label>
</div>
@@ -156,26 +156,26 @@
<div id="heating-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="heating-settings" class="hidden">
<div class="grid">
<label for="heating-min-temp">
<label>
<span data-i18n>settings.temp.min</span>
<input type="number" inputmode="numeric" id="heating-min-temp" name="heating[minTemp]" min="0" max="0" step="1" required>
<input type="number" inputmode="numeric" name="heating[minTemp]" min="0" max="0" step="1" required>
</label>
<label for="heating-max-temp">
<label>
<span data-i18n>settings.temp.max</span>
<input type="number" inputmode="numeric" id="heating-max-temp" name="heating[maxTemp]" min="0" max="0" step="1" required>
<input type="number" inputmode="numeric" name="heating[maxTemp]" min="0" max="0" step="1" required>
</label>
</div>
<div class="grid">
<label for="heating-hysteresis">
<label>
<span data-i18n>settings.heating.hyst</span>
<input type="number" inputmode="numeric" id="heating-hysteresis" name="heating[hysteresis]" min="0" max="5" step="0.05" required>
<input type="number" inputmode="numeric" name="heating[hysteresis]" min="0" max="5" step="0.05" required>
</label>
<label for="heating-turbo-factor">
<label>
<span data-i18n>settings.heating.turboFactor</span>
<input type="number" inputmode="numeric" id="heating-turbo-factor" name="heating[turboFactor]" min="1.5" max="10" step="0.1" required>
<input type="number" inputmode="numeric" name="heating[turboFactor]" min="1.5" max="10" step="0.1" required>
</label>
</div>
@@ -192,14 +192,14 @@
<div id="dhw-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="dhw-settings" class="hidden">
<div class="grid">
<label for="dhw-min-temp">
<label>
<span data-i18n>settings.temp.min</span>
<input type="number" inputmode="numeric" id="dhw-min-temp" name="dhw[minTemp]" min="0" max="0" step="1" required>
<input type="number" inputmode="numeric" name="dhw[minTemp]" min="0" max="0" step="1" required>
</label>
<label for="dhw-max-temp">
<label>
<span data-i18n>settings.temp.max</span>
<input type="number" inputmode="numeric" id="dhw-max-temp" name="dhw[maxTemp]" min="0" max="0" step="1" required>
<input type="number" inputmode="numeric" name="dhw[maxTemp]" min="0" max="0" step="1" required>
</label>
</div>
@@ -220,15 +220,15 @@
</fieldset>
<div class="grid">
<label for="emergency-target">
<label>
<span data-i18n>settings.emergency.target.title</span>
<input type="number" inputmode="numeric" id="emergency-target" name="emergency[target]" min="0" max="0" step="1" required>
<input type="number" inputmode="numeric" name="emergency[target]" min="0" max="0" step="1" required>
<small data-i18n>settings.emergency.target.note</small>
</label>
<label for="emergency-treshold-time">
<label>
<span data-i18n>settings.emergency.treshold</span>
<input type="number" inputmode="numeric" id="emergency-treshold-time" name="emergency[tresholdTime]" min="60" max="1800" step="1" required>
<input type="number" inputmode="numeric" name="emergency[tresholdTime]" min="60" max="1800" step="1" required>
</label>
</div>
@@ -245,26 +245,26 @@
<div id="equitherm-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="equitherm-settings" class="hidden">
<fieldset>
<label for="equitherm-enable">
<input type="checkbox" id="equitherm-enable" name="equitherm[enabled]" value="true">
<label>
<input type="checkbox" name="equitherm[enabled]" value="true">
<span data-i18n>settings.enable</span>
</label>
</fieldset>
<div class="grid">
<label for="equitherm-n-factor">
<label>
<span data-i18n>settings.equitherm.n</span>
<input type="number" inputmode="numeric" id="equitherm-n-factor" name="equitherm[n_factor]" min="0.001" max="10" step="0.001" required>
<input type="number" inputmode="numeric" name="equitherm[n_factor]" min="0.001" max="10" step="0.001" required>
</label>
<label for="equitherm-k-factor">
<label>
<span data-i18n>settings.equitherm.k</span>
<input type="number" inputmode="numeric" id="equitherm-k-factor" name="equitherm[k_factor]" min="0" max="10" step="0.01" required>
<input type="number" inputmode="numeric" name="equitherm[k_factor]" min="0" max="10" step="0.01" required>
</label>
<label for="equitherm-t-factor">
<label>
<span data-i18n>settings.equitherm.t.title</span>
<input type="number" inputmode="numeric" id="equitherm-t-factor" name="equitherm[t_factor]" min="0" max="10" step="0.01" required>
<input type="number" inputmode="numeric" name="equitherm[t_factor]" min="0" max="10" step="0.01" required>
<small data-i18n>settings.equitherm.t.note</small>
</label>
</div>
@@ -282,45 +282,45 @@
<div id="pid-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="pid-settings" class="hidden">
<fieldset>
<label for="pid-enable">
<input type="checkbox" id="pid-enable" name="pid[enabled]" value="true">
<label>
<input type="checkbox" name="pid[enabled]" value="true">
<span data-i18n>settings.enable</span>
</label>
</fieldset>
<div class="grid">
<label for="pid-p-factor">
<label>
<span data-i18n>settings.pid.p</span>
<input type="number" inputmode="numeric" id="pid-p-factor" name="pid[p_factor]" min="0.1" max="1000" step="0.01" required>
<input type="number" inputmode="numeric" name="pid[p_factor]" min="0.1" max="1000" step="0.01" required>
</label>
<label for="pid-i-factor">
<label>
<span data-i18n>settings.pid.i</span>
<input type="number" inputmode="numeric" id="pid-i-factor" name="pid[i_factor]" min="0" max="100" step="0.0001" required>
<input type="number" inputmode="numeric" name="pid[i_factor]" min="0" max="100" step="0.0001" required>
</label>
<label for="pid-d-factor">
<label>
<span data-i18n>settings.pid.d</span>
<input type="number" inputmode="numeric" id="pid-d-factor" name="pid[d_factor]" min="0" max="100000" step="0.1" required>
<input type="number" inputmode="numeric" name="pid[d_factor]" min="0" max="100000" step="0.1" required>
</label>
</div>
<label for="pid-dt">
<label>
<span data-i18n>settings.pid.dt</span>
<input type="number" inputmode="numeric" id="pid-dt" name="pid[dt]" min="30" max="1800" step="1" required>
<input type="number" inputmode="numeric" name="pid[dt]" min="30" max="1800" step="1" required>
</label>
<hr />
<div class="grid">
<label for="pid-min-temp">
<label>
<span data-i18n>settings.temp.min</span>
<input type="number" inputmode="numeric" id="pid-min-temp" name="pid[minTemp]" min="0" max="0" step="1" required>
<input type="number" inputmode="numeric" name="pid[minTemp]" min="0" max="0" step="1" required>
</label>
<label for="pid-max-temp">
<label>
<span data-i18n>settings.temp.max</span>
<input type="number" inputmode="numeric" id="pid-max-temp" name="pid[maxTemp]" min="0" max="0" step="1" required>
<input type="number" inputmode="numeric" name="pid[maxTemp]" min="0" max="0" step="1" required>
</label>
</div>
@@ -336,67 +336,67 @@
<details>
<summary><b data-i18n>settings.section.ot</b></summary>
<div>
<div id="opentherm-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="opentherm-settings" class="hidden">
<div id="ot-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="ot-settings" class="hidden">
<fieldset>
<legend data-i18n>settings.system.unit</legend>
<label>
<input type="radio" class="opentherm-unit-system" name="opentherm[unitSystem]" value="0" />
<input type="radio" name="opentherm[unitSystem]" value="0" />
<span data-i18n>settings.system.metric</span>
</label>
<label>
<input type="radio" class="opentherm-unit-system" name="opentherm[unitSystem]" value="1" />
<input type="radio" name="opentherm[unitSystem]" value="1" />
<span data-i18n>settings.system.imperial</span>
</label>
</fieldset>
<div class="grid">
<label for="opentherm-in-gpio">
<label>
<span data-i18n>settings.ot.inGpio</span>
<input type="number" inputmode="numeric" id="opentherm-in-gpio" name="opentherm[inGpio]" min="0" max="254" step="1">
<input type="number" inputmode="numeric" name="opentherm[inGpio]" min="0" max="254" step="1">
</label>
<label for="opentherm-in-gpio">
<label>
<span data-i18n>settings.ot.outGpio</span>
<input type="number" inputmode="numeric" id="opentherm-out-gpio" name="opentherm[outGpio]" min="0" max="254" step="1">
<input type="number" inputmode="numeric" name="opentherm[outGpio]" min="0" max="254" step="1">
</label>
<label for="opentherm-rx-led-gpio">
<label>
<span data-i18n>settings.ot.ledGpio</span>
<input type="number" inputmode="numeric" id="opentherm-rx-led-gpio" name="opentherm[rxLedGpio]" min="0" max="254" step="1">
<input type="number" inputmode="numeric" name="opentherm[rxLedGpio]" min="0" max="254" step="1">
<small data-i18n>settings.note.blankNotUse</small>
</label>
</div>
<div class="grid">
<label for="opentherm-member-id-code">
<label>
<span data-i18n>settings.ot.memberId</span>
<input type="number" inputmode="numeric" id="opentherm-member-id" name="opentherm[memberId]" min="0" max="255" step="1" required>
<input type="number" inputmode="numeric" name="opentherm[memberId]" min="0" max="255" step="1" required>
</label>
<label for="opentherm-flags">
<label>
<span data-i18n>settings.ot.flags</span>
<input type="number" inputmode="numeric" id="opentherm-flags" name="opentherm[flags]" min="0" max="255" step="1" required>
<input type="number" inputmode="numeric" name="opentherm[flags]" min="0" max="255" step="1" required>
</label>
<label for="opentherm-max-modulation">
<label>
<span data-i18n>settings.ot.maxMod</span>
<input type="number" inputmode="numeric" id="opentherm-max-modulation" name="opentherm[maxModulation]" min="1" max="100" step="1" required>
<input type="number" inputmode="numeric" name="opentherm[maxModulation]" min="1" max="100" step="1" required>
</label>
</div>
<div class="grid">
<label for="opentherm-min-power">
<label>
<span data-i18n>settings.ot.minPower.title</span>
<input type="number" inputmode="numeric" id="opentherm-min-power" name="opentherm[minPower]" min="0" max="1000" step="0.1">
<input type="number" inputmode="numeric" name="opentherm[minPower]" min="0" max="1000" step="0.1">
<small data-i18n>settings.ot.minPower.note</small>
</label>
<label for="opentherm-max-power">
<label>
<span data-i18n>settings.ot.maxPower.title</span>
<input type="number" inputmode="numeric" id="opentherm-max-power" name="opentherm[maxPower]" min="0" max="1000" step="0.1">
<input type="number" inputmode="numeric" name="opentherm[maxPower]" min="0" max="1000" step="0.1">
<small data-i18n>settings.ot.maxPower.note</small>
</label>
</div>
@@ -404,54 +404,59 @@
<fieldset>
<legend data-i18n>settings.ot.options.desc</legend>
<label for="opentherm-dhw-present">
<input type="checkbox" id="opentherm-dhw-present" name="opentherm[dhwPresent]" value="true">
<span data-i18n>settings.ot.options.dhwPresent</span>
<label>
<input type="checkbox" name="opentherm[options][dhwSupport]" value="true">
<span data-i18n>settings.ot.options.dhwSupport</span>
</label>
<label for="opentherm-sw-mode">
<input type="checkbox" id="opentherm-sw-mode" name="opentherm[summerWinterMode]" value="true">
<label>
<input type="checkbox" name="opentherm[options][coolingSupport]" value="true">
<span data-i18n>settings.ot.options.coolingSupport</span>
</label>
<label>
<input type="checkbox" name="opentherm[options][summerWinterMode]" value="true">
<span data-i18n>settings.ot.options.summerWinterMode</span>
</label>
<label for="opentherm-heating-ch2-enabled">
<input type="checkbox" id="opentherm-heating-ch2-enabled" name="opentherm[heatingCh2Enabled]" value="true">
<span data-i18n>settings.ot.options.heatingCh2Enabled</span>
<label>
<input type="checkbox" name="opentherm[options][ch2AlwaysEnabled]" value="true">
<span data-i18n>settings.ot.options.ch2AlwaysEnabled</span>
</label>
<label for="opentherm-heating-ch1-to-ch2">
<input type="checkbox" id="opentherm-heating-ch1-to-ch2" name="opentherm[heatingCh1ToCh2]" value="true">
<span data-i18n>settings.ot.options.heatingCh1ToCh2</span>
<label>
<input type="checkbox" name="opentherm[options][heatingToCh2]" value="true">
<span data-i18n>settings.ot.options.heatingToCh2</span>
</label>
<label for="opentherm-dhw-to-ch2">
<input type="checkbox" id="opentherm-dhw-to-ch2" name="opentherm[dhwToCh2]" value="true">
<label>
<input type="checkbox" name="opentherm[options][dhwToCh2]" value="true">
<span data-i18n>settings.ot.options.dhwToCh2</span>
</label>
<label for="opentherm-dhw-blocking">
<input type="checkbox" id="opentherm-dhw-blocking" name="opentherm[dhwBlocking]" value="true">
<label>
<input type="checkbox" name="opentherm[options][dhwBlocking]" value="true">
<span data-i18n>settings.ot.options.dhwBlocking</span>
</label>
<label for="opentherm-sync-modulation-with-heating">
<input type="checkbox" id="opentherm-sync-modulation-with-heating" name="opentherm[modulationSyncWithHeating]" value="true">
<label>
<input type="checkbox" name="opentherm[options][modulationSyncWithHeating]" value="true">
<span data-i18n>settings.ot.options.modulationSyncWithHeating</span>
</label>
<label for="opentherm-get-min-max-temp">
<input type="checkbox" id="opentherm-get-min-max-temp" name="opentherm[getMinMaxTemp]" value="true">
<label>
<input type="checkbox" name="opentherm[options][getMinMaxTemp]" value="true">
<span data-i18n>settings.ot.options.getMinMaxTemp</span>
</label>
<label for="opentherm-immergas-fix">
<input type="checkbox" id="opentherm-immergas-fix" name="opentherm[immergasFix]" value="true">
<label>
<input type="checkbox" name="opentherm[options][immergasFix]" value="true">
<span data-i18n>settings.ot.options.immergasFix</span>
</label>
<hr />
<label for="opentherm-native-heating-control">
<input type="checkbox" id="opentherm-native-heating-control" name="opentherm[nativeHeatingControl]" value="true">
<label>
<input type="checkbox" name="opentherm[options][nativeHeatingControl]" value="true">
<span data-i18n>settings.ot.nativeHeating.title</span><br />
<small data-i18n>settings.ot.nativeHeating.note</small>
</label>
@@ -470,50 +475,50 @@
<div id="mqtt-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="mqtt-settings" class="hidden">
<fieldset>
<label for="mqtt-enable">
<input type="checkbox" id="mqtt-enable" name="mqtt[enabled]" value="true">
<label>
<input type="checkbox" name="mqtt[enabled]" value="true">
<span data-i18n>settings.enable</span>
</label>
<label for="mqtt-ha-discovery">
<input type="checkbox" id="mqtt-ha-discovery" name="mqtt[homeAssistantDiscovery]" value="true">
<label>
<input type="checkbox" name="mqtt[homeAssistantDiscovery]" value="true">
<span data-i18n>settings.mqtt.homeAssistantDiscovery</span>
</label>
</fieldset>
<div class="grid">
<label for="mqtt-server">
<label>
<span data-i18n>settings.mqtt.server</span>
<input type="text" id="mqtt-server" name="mqtt[server]" maxlength="80" required>
<input type="text" name="mqtt[server]" maxlength="80" required>
</label>
<label for="mqtt-port">
<label>
<span data-i18n>settings.mqtt.port</span>
<input type="number" inputmode="numeric" id="mqtt-port" name="mqtt[port]" min="1" max="65535" step="1" required>
<input type="number" inputmode="numeric" name="mqtt[port]" min="1" max="65535" step="1" required>
</label>
</div>
<div class="grid">
<label for="mqtt-user">
<label>
<span data-i18n>settings.mqtt.user</span>
<input type="text" id="mqtt-user" name="mqtt[user]" maxlength="32" required>
<input type="text" name="mqtt[user]" maxlength="32" required>
</label>
<label for="mqtt-password">
<label>
<span data-i18n>settings.mqtt.password</span>
<input type="password" id="mqtt-password" name="mqtt[password]" maxlength="32">
<input type="password" name="mqtt[password]" maxlength="32">
</label>
</div>
<div class="grid">
<label for="mqtt-prefix">
<label>
<span data-i18n>settings.mqtt.prefix</span>
<input type="text" id="mqtt-prefix" name="mqtt[prefix]" maxlength="32" required>
<input type="text" name="mqtt[prefix]" maxlength="32" required>
</label>
<label for="mqtt-interval">
<label>
<span data-i18n>settings.mqtt.interval</span>
<input type="number" inputmode="numeric" id="mqtt-interval" name="mqtt[interval]" min="3" max="60" step="1" required>
<input type="number" inputmode="numeric" name="mqtt[interval]" min="3" max="60" step="1" required>
</label>
</div>
@@ -530,33 +535,33 @@
<div id="extpump-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="extpump-settings" class="hidden">
<fieldset>
<label for="extpump-use">
<input type="checkbox" id="extpump-use" name="externalPump[use]" value="true">
<label>
<input type="checkbox" name="externalPump[use]" value="true">
<span data-i18n>settings.extPump.use</span>
</label>
</fieldset>
<div class="grid">
<label for="extpump-gpio">
<label>
<span data-i18n>settings.extPump.gpio</span>
<input type="number" inputmode="numeric" id="extpump-gpio" name="externalPump[gpio]" min="0" max="254" step="1">
<input type="number" inputmode="numeric" name="externalPump[gpio]" min="0" max="254" step="1">
</label>
<label for="extpump-pc-time">
<label>
<span data-i18n>settings.extPump.postCirculationTime</span>
<input type="number" inputmode="numeric" id="extpump-pc-time" name="externalPump[postCirculationTime]" min="1" max="120" step="1" required>
<input type="number" inputmode="numeric" name="externalPump[postCirculationTime]" min="1" max="120" step="1" required>
</label>
</div>
<div class="grid">
<label for="extpump-as-interval">
<label>
<span data-i18n>settings.extPump.antiStuckInterval</span>
<input type="number" inputmode="numeric" id="extpump-as-interval" name="externalPump[antiStuckInterval]" min="1" max="366" step="1" required>
<input type="number" inputmode="numeric" name="externalPump[antiStuckInterval]" min="1" max="366" step="1" required>
</label>
<label for="extpump-as-time">
<label>
<span data-i18n>settings.extPump.antiStuckTime</span>
<input type="number" inputmode="numeric" id="extpump-as-time" name="externalPump[antiStuckTime]" min="1" max="20" step="1" required>
<input type="number" inputmode="numeric" name="externalPump[antiStuckTime]" min="1" max="20" step="1" required>
</label>
</div>
@@ -573,74 +578,74 @@
<div id="cc-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="cc-settings" class="hidden">
<fieldset>
<label for="cc-input-enable">
<input type="checkbox" id="cc-input-enable" name="cascadeControl[input][enabled]" value="true">
<label>
<input type="checkbox" name="cascadeControl[input][enabled]" value="true">
<span data-i18n>settings.cascadeControl.input.enable</span>
<br />
<small data-i18n>settings.cascadeControl.input.desc</small>
</label>
<label for="cc-input-invert-state">
<input type="checkbox" id="cc-input-invert-state" name="cascadeControl[input][invertState]" value="true">
<label>
<input type="checkbox" name="cascadeControl[input][invertState]" value="true">
<span data-i18n>settings.cascadeControl.input.invertState</span>
</label>
</fieldset>
<div class="grid">
<label for="cc-input-gpio">
<label>
<span data-i18n>settings.cascadeControl.input.gpio</span>
<input type="number" inputmode="numeric" id="cc-input-gpio" name="cascadeControl[input][gpio]" min="0" max="254" step="1">
<input type="number" inputmode="numeric" name="cascadeControl[input][gpio]" min="0" max="254" step="1">
</label>
<label for="cc-input-tt">
<label>
<span data-i18n>settings.cascadeControl.input.thresholdTime</span>
<input type="number" inputmode="numeric" id="cc-input-tt" name="cascadeControl[input][thresholdTime]" min="5" max="600" step="1" required>
<input type="number" inputmode="numeric" name="cascadeControl[input][thresholdTime]" min="5" max="600" step="1" required>
</label>
</div>
<hr />
<fieldset>
<label for="cc-output-enable">
<input type="checkbox" id="cc-output-enable" name="cascadeControl[output][enabled]" value="true">
<label>
<input type="checkbox" name="cascadeControl[output][enabled]" value="true">
<span data-i18n>settings.cascadeControl.output.enable</span>
<br />
<small data-i18n>settings.cascadeControl.output.desc</small>
</label>
<label for="cc-output-invert-state">
<input type="checkbox" id="cc-output-invert-state" name="cascadeControl[output][invertState]" value="true">
<label>
<input type="checkbox" name="cascadeControl[output][invertState]" value="true">
<span data-i18n>settings.cascadeControl.output.invertState</span>
</label>
</fieldset>
<div class="grid">
<label for="cc-output-gpio">
<label>
<span data-i18n>settings.cascadeControl.output.gpio</span>
<input type="number" outputmode="numeric" id="cc-output-gpio" name="cascadeControl[output][gpio]" min="0" max="254" step="1">
<input type="number" outputmode="numeric" name="cascadeControl[output][gpio]" min="0" max="254" step="1">
</label>
<label for="cc-output-tt">
<label>
<span data-i18n>settings.cascadeControl.output.thresholdTime</span>
<input type="number" outputmode="numeric" id="cc-output-tt" name="cascadeControl[output][thresholdTime]" min="5" max="600" step="1" required>
<input type="number" outputmode="numeric" name="cascadeControl[output][thresholdTime]" min="5" max="600" step="1" required>
</label>
</div>
<fieldset>
<legend data-i18n>settings.cascadeControl.output.events.desc</legend>
<label for="cc-on-fault">
<input type="checkbox" id="cc-on-fault" name="cascadeControl[output][onFault]" value="true">
<label>
<input type="checkbox" name="cascadeControl[output][onFault]" value="true">
<span data-i18n>settings.cascadeControl.output.events.onFault</span>
</label>
<label for="cc-on-loss-conn">
<input type="checkbox" id="cc-on-loss-conn" name="cascadeControl[output][onLossConnection]" value="true">
<label>
<input type="checkbox" name="cascadeControl[output][onLossConnection]" value="true">
<span data-i18n>settings.cascadeControl.output.events.onLossConnection</span>
</label>
<label for="cc-on-enabled-heating">
<input type="checkbox" id="cc-on-enabled-heating" name="cascadeControl[output][onEnabledHeating]" value="true">
<label>
<input type="checkbox" name="cascadeControl[output][onEnabledHeating]" value="true">
<span data-i18n>settings.cascadeControl.output.events.onEnabledHeating</span>
</label>
</fieldset>
@@ -671,111 +676,112 @@
const fillData = (data) => {
// System
setSelectValue('#system-log-level', data.system.logLevel);
setCheckboxValue('#system-serial-enable', data.system.serial.enabled);
setSelectValue('#system-serial-baudrate', data.system.serial.baudrate);
setCheckboxValue('#system-telnet-enable', data.system.telnet.enabled);
setInputValue('#system-telnet-port', data.system.telnet.port);
setRadioValue('.system-unit-system', data.system.unitSystem);
setInputValue('#system-status-led-gpio', data.system.statusLedGpio < 255 ? data.system.statusLedGpio : '');
setSelectValue("[name='system[logLevel]']", data.system.logLevel);
setCheckboxValue("[name='system[serial][enabled]']", data.system.serial.enabled);
setSelectValue("[name='system[serial][baudrate]']", data.system.serial.baudrate);
setCheckboxValue("[name='system[telnet][enabled]']", data.system.telnet.enabled);
setInputValue("[name='system[telnet][port]']", data.system.telnet.port);
setRadioValue("[name='system[unitSystem]']", data.system.unitSystem);
setInputValue("[name='system[statusLedGpio]']", data.system.statusLedGpio < 255 ? data.system.statusLedGpio : '');
setBusy('#system-settings-busy', '#system-settings', false);
// Portal
setCheckboxValue('#portal-auth', data.portal.auth);
setInputValue('#portal-login', data.portal.login);
setInputValue('#portal-password', data.portal.password);
setCheckboxValue("[name='portal[auth]']", data.portal.auth);
setInputValue("[name='portal[login]']", data.portal.login);
setInputValue("[name='portal[password]']", data.portal.password);
setBusy('#portal-settings-busy', '#portal-settings', false);
// Opentherm
setRadioValue('.opentherm-unit-system', data.opentherm.unitSystem);
setInputValue('#opentherm-in-gpio', data.opentherm.inGpio < 255 ? data.opentherm.inGpio : '');
setInputValue('#opentherm-out-gpio', data.opentherm.outGpio < 255 ? data.opentherm.outGpio : '');
setInputValue('#opentherm-rx-led-gpio', data.opentherm.rxLedGpio < 255 ? data.opentherm.rxLedGpio : '');
setInputValue('#opentherm-member-id', data.opentherm.memberId);
setInputValue('#opentherm-flags', data.opentherm.flags);
setInputValue('#opentherm-max-modulation', data.opentherm.maxModulation);
setInputValue('#opentherm-min-power', data.opentherm.minPower);
setInputValue('#opentherm-max-power', data.opentherm.maxPower);
setCheckboxValue('#opentherm-dhw-present', data.opentherm.dhwPresent);
setCheckboxValue('#opentherm-sw-mode', data.opentherm.summerWinterMode);
setCheckboxValue('#opentherm-heating-ch2-enabled', data.opentherm.heatingCh2Enabled);
setCheckboxValue('#opentherm-heating-ch1-to-ch2', data.opentherm.heatingCh1ToCh2);
setCheckboxValue('#opentherm-dhw-to-ch2', data.opentherm.dhwToCh2);
setCheckboxValue('#opentherm-dhw-blocking', data.opentherm.dhwBlocking);
setCheckboxValue('#opentherm-sync-modulation-with-heating', data.opentherm.modulationSyncWithHeating);
setCheckboxValue('#opentherm-get-min-max-temp', data.opentherm.getMinMaxTemp);
setCheckboxValue('#opentherm-native-heating-control', data.opentherm.nativeHeatingControl);
setCheckboxValue('#opentherm-immergas-fix', data.opentherm.immergasFix);
setBusy('#opentherm-settings-busy', '#opentherm-settings', false);
setRadioValue("[name='opentherm[unitSystem]']", data.opentherm.unitSystem);
setInputValue("[name='opentherm[inGpio]']", data.opentherm.inGpio < 255 ? data.opentherm.inGpio : '');
setInputValue("[name='opentherm[outGpio]']", data.opentherm.outGpio < 255 ? data.opentherm.outGpio : '');
setInputValue("[name='opentherm[rxLedGpio]']", data.opentherm.rxLedGpio < 255 ? data.opentherm.rxLedGpio : '');
setInputValue("[name='opentherm[memberId]']", data.opentherm.memberId);
setInputValue("[name='opentherm[flags]']", data.opentherm.flags);
setInputValue("[name='opentherm[maxModulation]']", data.opentherm.maxModulation);
setInputValue("[name='opentherm[minPower]']", data.opentherm.minPower);
setInputValue("[name='opentherm[maxPower]']", data.opentherm.maxPower);
setCheckboxValue("[name='opentherm[options][dhwSupport]']", data.opentherm.options.dhwSupport);
setCheckboxValue("[name='opentherm[options][coolingSupport]']", data.opentherm.options.coolingSupport);
setCheckboxValue("[name='opentherm[options][summerWinterMode]']", data.opentherm.options.summerWinterMode);
setCheckboxValue("[name='opentherm[options][ch2AlwaysEnabled]']", data.opentherm.options.ch2AlwaysEnabled);
setCheckboxValue("[name='opentherm[options][heatingToCh2]']", data.opentherm.options.heatingToCh2);
setCheckboxValue("[name='opentherm[options][dhwToCh2]']", data.opentherm.options.dhwToCh2);
setCheckboxValue("[name='opentherm[options][dhwBlocking]']", data.opentherm.options.dhwBlocking);
setCheckboxValue("[name='opentherm[options][modulationSyncWithHeating]']", data.opentherm.options.modulationSyncWithHeating);
setCheckboxValue("[name='opentherm[options][getMinMaxTemp]']", data.opentherm.options.getMinMaxTemp);
setCheckboxValue("[name='opentherm[options][nativeHeatingControl]']", data.opentherm.options.nativeHeatingControl);
setCheckboxValue("[name='opentherm[options][immergasFix]']", data.opentherm.options.immergasFix);
setBusy('#ot-settings-busy', '#ot-settings', false);
// MQTT
setCheckboxValue('#mqtt-enable', data.mqtt.enabled);
setCheckboxValue('#mqtt-ha-discovery', data.mqtt.homeAssistantDiscovery);
setInputValue('#mqtt-server', data.mqtt.server);
setInputValue('#mqtt-port', data.mqtt.port);
setInputValue('#mqtt-user', data.mqtt.user);
setInputValue('#mqtt-password', data.mqtt.password);
setInputValue('#mqtt-prefix', data.mqtt.prefix);
setInputValue('#mqtt-interval', data.mqtt.interval);
setCheckboxValue("[name='mqtt[enabled]']", data.mqtt.enabled);
setCheckboxValue("[name='mqtt[homeAssistantDiscovery]']", data.mqtt.homeAssistantDiscovery);
setInputValue("[name='mqtt[server]']", data.mqtt.server);
setInputValue("[name='mqtt[port]']", data.mqtt.port);
setInputValue("[name='mqtt[user]']", data.mqtt.user);
setInputValue("[name='mqtt[password]']", data.mqtt.password);
setInputValue("[name='mqtt[prefix]']", data.mqtt.prefix);
setInputValue("[name='mqtt[interval]']", data.mqtt.interval);
setBusy('#mqtt-settings-busy', '#mqtt-settings', false);
// Extpump
setCheckboxValue('#extpump-use', data.externalPump.use);
setInputValue('#extpump-gpio', data.externalPump.gpio < 255 ? data.externalPump.gpio : '');
setInputValue('#extpump-pc-time', data.externalPump.postCirculationTime);
setInputValue('#extpump-as-interval', data.externalPump.antiStuckInterval);
setInputValue('#extpump-as-time', data.externalPump.antiStuckTime);
setCheckboxValue("[name='externalPump[use]']", data.externalPump.use);
setInputValue("[name='externalPump[gpio]']", data.externalPump.gpio < 255 ? data.externalPump.gpio : '');
setInputValue("[name='externalPump[postCirculationTime]']", data.externalPump.postCirculationTime);
setInputValue("[name='externalPump[antiStuckInterval]']", data.externalPump.antiStuckInterval);
setInputValue("[name='externalPump[antiStuckTime]']", data.externalPump.antiStuckTime);
setBusy('#extpump-settings-busy', '#extpump-settings', false);
// Cascade control
setCheckboxValue('#cc-input-enable', data.cascadeControl.input.enabled);
setInputValue('#cc-input-gpio', data.cascadeControl.input.gpio < 255 ? data.cascadeControl.input.gpio : '');
setCheckboxValue('#cc-input-invert-state', data.cascadeControl.input.invertState);
setInputValue('#cc-input-tt', data.cascadeControl.input.thresholdTime);
setCheckboxValue("[name='cascadeControl[input][enabled]']", data.cascadeControl.input.enabled);
setInputValue("[name='cascadeControl[input][gpio]']", data.cascadeControl.input.gpio < 255 ? data.cascadeControl.input.gpio : '');
setCheckboxValue("[name='cascadeControl[input][invertState]']", data.cascadeControl.input.invertState);
setInputValue("[name='cascadeControl[input][thresholdTime]']", data.cascadeControl.input.thresholdTime);
setCheckboxValue('#cc-output-enable', data.cascadeControl.output.enabled);
setInputValue('#cc-output-gpio', data.cascadeControl.output.gpio < 255 ? data.cascadeControl.output.gpio : '');
setCheckboxValue('#cc-output-invert-state', data.cascadeControl.output.invertState);
setInputValue('#cc-output-tt', data.cascadeControl.output.thresholdTime);
setCheckboxValue('#cc-on-fault', data.cascadeControl.output.onFault);
setCheckboxValue('#cc-on-loss-conn', data.cascadeControl.output.onLossConnection);
setCheckboxValue('#cc-on-enabled-heating', data.cascadeControl.output.onEnabledHeating);
setCheckboxValue("[name='cascadeControl[output][enabled]']", data.cascadeControl.output.enabled);
setInputValue("[name='cascadeControl[output][gpio]']", data.cascadeControl.output.gpio < 255 ? data.cascadeControl.output.gpio : '');
setCheckboxValue("[name='cascadeControl[output][invertState]']", data.cascadeControl.output.invertState);
setInputValue("[name='cascadeControl[output][thresholdTime]']", data.cascadeControl.output.thresholdTime);
setCheckboxValue("[name='cascadeControl[output][onFault]']", data.cascadeControl.output.onFault);
setCheckboxValue("[name='cascadeControl[output][onLossConnection]']", data.cascadeControl.output.onLossConnection);
setCheckboxValue("[name='cascadeControl[output][onEnabledHeating]']", data.cascadeControl.output.onEnabledHeating);
setBusy('#cc-settings-busy', '#cc-settings', false);
// Heating
setInputValue('#heating-min-temp', data.heating.minTemp, {
setInputValue("[name='heating[minTemp]']", data.heating.minTemp, {
"min": data.system.unitSystem == 0 ? 0 : 32,
"max": data.system.unitSystem == 0 ? 99 : 211
});
setInputValue('#heating-max-temp', data.heating.maxTemp, {
setInputValue("[name='heating[maxTemp]']", data.heating.maxTemp, {
"min": data.system.unitSystem == 0 ? 1 : 33,
"max": data.system.unitSystem == 0 ? 100 : 212
});
setInputValue('#heating-hysteresis', data.heating.hysteresis);
setInputValue('#heating-turbo-factor', data.heating.turboFactor);
setInputValue("[name='heating[hysteresis]']", data.heating.hysteresis);
setInputValue("[name='heating[turboFactor]']", data.heating.turboFactor);
setBusy('#heating-settings-busy', '#heating-settings', false);
// DHW
setInputValue('#dhw-min-temp', data.dhw.minTemp, {
setInputValue("[name='dhw[minTemp]']", data.dhw.minTemp, {
"min": data.system.unitSystem == 0 ? 0 : 32,
"max": data.system.unitSystem == 0 ? 99 : 211
});
setInputValue('#dhw-max-temp', data.dhw.maxTemp, {
setInputValue("[name='dhw[maxTemp]']", data.dhw.maxTemp, {
"min": data.system.unitSystem == 0 ? 1 : 33,
"max": data.system.unitSystem == 0 ? 100 : 212
});
setBusy('#dhw-settings-busy', '#dhw-settings', false);
// Emergency mode
setInputValue('#emergency-treshold-time', data.emergency.tresholdTime);
if (data.opentherm.nativeHeatingControl) {
setInputValue('#emergency-target', data.emergency.target, {
setInputValue("[name='emergency[tresholdTime]']", data.emergency.tresholdTime);
if (data.opentherm.options.nativeHeatingControl) {
setInputValue("[name='emergency[target]']", data.emergency.target, {
"min": data.system.unitSystem == 0 ? 5 : 41,
"max": data.system.unitSystem == 0 ? 30 : 86
});
} else {
setInputValue('#emergency-target', data.emergency.target, {
setInputValue("[name='emergency[target]']", data.emergency.target, {
"min": data.heating.minTemp,
"max": data.heating.maxTemp,
});
@@ -784,23 +790,23 @@
setBusy('#emergency-settings-busy', '#emergency-settings', false);
// Equitherm
setCheckboxValue('#equitherm-enable', data.equitherm.enabled);
setInputValue('#equitherm-n-factor', data.equitherm.n_factor);
setInputValue('#equitherm-k-factor', data.equitherm.k_factor);
setInputValue('#equitherm-t-factor', data.equitherm.t_factor);
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[t_factor]']", data.equitherm.t_factor);
setBusy('#equitherm-settings-busy', '#equitherm-settings', false);
// PID
setCheckboxValue('#pid-enable', data.pid.enabled);
setInputValue('#pid-p-factor', data.pid.p_factor);
setInputValue('#pid-i-factor', data.pid.i_factor);
setInputValue('#pid-d-factor', data.pid.d_factor);
setInputValue('#pid-dt', data.pid.dt);
setInputValue('#pid-min-temp', data.pid.minTemp, {
setCheckboxValue("[name='pid[enabled]']", data.pid.enabled);
setInputValue("[name='pid[p_factor]']", data.pid.p_factor);
setInputValue("[name='pid[i_factor]']", data.pid.i_factor);
setInputValue("[name='pid[d_factor]']", data.pid.d_factor);
setInputValue("[name='pid[dt]']", data.pid.dt);
setInputValue("[name='pid[minTemp]']", data.pid.minTemp, {
"min": data.equitherm.enabled ? (data.system.unitSystem == 0 ? -100 : -146) : (data.system.unitSystem == 0 ? 0 : 32),
"max": (data.system.unitSystem == 0 ? 99 : 211)
});
setInputValue('#pid-max-temp', data.pid.maxTemp, {
setInputValue("[name='pid[maxTemp]']", data.pid.maxTemp, {
"min": (data.system.unitSystem == 0 ? 0 : 33),
"max": (data.system.unitSystem == 0 ? 100 : 212)
});
@@ -827,7 +833,7 @@
setupForm('#emergency-settings', fillData);
setupForm('#equitherm-settings', fillData);
setupForm('#pid-settings', fillData);
setupForm('#opentherm-settings', fillData);
setupForm('#ot-settings', fillData);
setupForm('#mqtt-settings', fillData, ['mqtt.user', 'mqtt.password', 'mqtt.prefix']);
setupForm('#extpump-settings', fillData);
setupForm('#cc-settings', fillData);

View File

@@ -42,7 +42,7 @@
<input type="file" name="settings" id="restore-file" accept=".json">
</label>
<div class="grid">
<div role="group">
<button type="submit" data-i18n>button.restore</button>
<button type="button" class="secondary" onclick="window.location='/api/backup/save';" data-i18n>button.backup</button>
</div>