24 Commits

Author SHA1 Message Date
Yurii
b89f61ed58 chore: bump version 2024-04-06 20:28:41 +03:00
Yurii
ab1566bd45 fix: memory leak on esp32 fixed 2024-04-06 20:28:05 +03:00
Yurii
86734ab622 refactor: update portal (unit system) 2024-04-06 18:25:30 +03:00
Yurii
0a8dd2a076 feat: added support unit systems for pressure and flow rate 2024-04-06 18:19:06 +03:00
Yurii
a7a561622e Merge branch 'unit-system' 2024-04-06 17:38:24 +03:00
Yurii
b0e0f6fd7d feat: added setting to enable/disable polling of min and max temperatures via opentherm 2024-04-06 15:51:49 +03:00
Yurii
53eaa1d7f1 chore: bump version 2024-04-04 21:26:48 +03:00
Yurii
a7d796e0cc refactor: removed unused methods, replaced some methods to native 2024-03-31 22:29:53 +03:00
Yurii
4490b38130 fix: fixed reading exhaust gas temperature 2024-03-31 19:41:33 +03:00
Yurii
0ede2240a2 fix: temperature_unit for climate fixed; temp in vars.parameters.* by default fixed 2024-03-31 07:29:32 +03:00
Yurii
0cff35ee12 feat: update portal for unit systems 2024-03-31 06:32:23 +03:00
Yurii
14aef20234 fix: typo for HA_TEMPERATURE_UNIT 2024-03-31 02:49:36 +03:00
Yurii
560f8fbd51 feat: optimizing with different unit systems 2024-03-31 02:47:20 +03:00
Yurii
946414ad31 Merge branch 'master' into unit-system 2024-03-31 01:02:59 +03:00
Yurii
39a29042e1 fix: set max temp (ID57) as setpoint heating temp 2024-03-31 00:37:18 +03:00
Yurii
f544f01caa feat: polling of exhaust gas temperature (#42) and heating return temperature; added new sensors to HA 2024-03-30 00:04:51 +03:00
Yurii
41cca76bfa chore: update README 2024-03-24 20:38:02 +03:00
Yurii
942bc53043 chore: bump version 2024-03-24 19:28:26 +03:00
Yurii
1bad689b6b fix: revert board_build.ldscript for esp8266, update OpenTherm Library 2024-03-24 19:27:16 +03:00
Yurii
2f4dbcc205 feat: added unit system selection 2024-03-20 02:37:20 +03:00
Yurii
9e3ef7a465 chore: bump version 2024-03-14 13:08:11 +03:00
Yurii
a5f6749101 refactor: added SensorType enum 2024-03-14 13:07:42 +03:00
Yurii
b07dd46f55 refactor: optimization
* names changed: pin => gpio
* ability to change OpenTherm GPIO without rebooting
2024-03-10 04:10:18 +03:00
Yurii
07ab121788 chore: bump OpenTherm Library to master 2024-03-09 00:03:34 +03:00
21 changed files with 1297 additions and 584 deletions

View File

@@ -2,7 +2,7 @@
![logo](/assets/logo.svg) ![logo](/assets/logo.svg)
<br> <br>
[![GitHub version](https://img.shields.io/github/release/Laxilef/OTGateway.svg)](https://github.com/Laxilef/OTGateway/releases) [![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) [![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) [![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) [![Telegram](https://img.shields.io/badge/Telegram-Channel-33A8E3)](https://t.me/otgateway)

View File

@@ -168,11 +168,11 @@
</tr> </tr>
<tr> <tr>
<th scope="row">Pressure:</th> <th scope="row">Pressure:</th>
<td><b class="ot-pressure"></b> bar</td> <td><b class="ot-pressure"></b> <span class="pressure-unit"></span></td>
</tr> </tr>
<tr> <tr>
<th scope="row">DHW flow rate:</th> <th scope="row">DHW flow rate:</th>
<td><b class="ot-dhw-flow-rate"></b> l/min</td> <td><b class="ot-dhw-flow-rate"></b> <span class="volume-unit"></span>/min</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Fault code:</th> <th scope="row">Fault code:</th>
@@ -180,23 +180,31 @@
</tr> </tr>
<tr> <tr>
<th scope="row">Indoor temp:</th> <th scope="row">Indoor temp:</th>
<td><b class="indoor-temp"></b> C</td> <td><b class="indoor-temp"></b> <span class="temp-unit"></span></td>
</tr> </tr>
<tr> <tr>
<th scope="row">Outdoor temp:</th> <th scope="row">Outdoor temp:</th>
<td><b class="outdoor-temp"></b> C</td> <td><b class="outdoor-temp"></b> <span class="temp-unit"></span></td>
</tr> </tr>
<tr> <tr>
<th scope="row">Heating temp:</th> <th scope="row">Heating temp:</th>
<td><b class="heating-temp"></b> C</td> <td><b class="heating-temp"></b> <span class="temp-unit"></span></td>
</tr> </tr>
<tr> <tr>
<th scope="row">Heating setpoint temp:</th> <th scope="row">Heating setpoint temp:</th>
<td><b class="heating-setpoint-temp"></b> C</td> <td><b class="heating-setpoint-temp"></b> <span class="temp-unit"></span></td>
</tr>
<tr>
<th scope="row">Heating return temp:</th>
<td><b class="heating-return-temp"></b> <span class="temp-unit"></span></td>
</tr> </tr>
<tr> <tr>
<th scope="row">DHW temp:</th> <th scope="row">DHW temp:</th>
<td><b class="dhw-temp"></b> C</td> <td><b class="dhw-temp"></b> <span class="temp-unit"></span></td>
</tr>
<tr>
<th scope="row">Exhaust temp:</th>
<td><b class="exhaust-temp"></b> <span class="temp-unit"></span></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -62,14 +62,26 @@
<div id="opentherm-settings-busy" aria-busy="true"></div> <div id="opentherm-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="opentherm-settings" class="hidden"> <form action="/api/settings" id="opentherm-settings" class="hidden">
<div class="grid"> <fieldset>
<label for="opentherm-in-pin"> <legend>Unit system</legend>
In GPIO <label>
<input type="number" inputmode="numeric" class="opentherm-in-pin" name="opentherm[inPin]" min="0" max="99" step="1" required> <input type="radio" class="opentherm-unit-system" name="opentherm[unitSystem]" value="0" />
Metric (celsius)
</label> </label>
<label for="opentherm-in-pin"> <label>
<input type="radio" class="opentherm-unit-system" name="opentherm[unitSystem]" value="1" />
Imperial (fahrenheit)
</label>
</fieldset>
<div class="grid">
<label for="opentherm-in-gpio">
In GPIO
<input type="number" inputmode="numeric" class="opentherm-in-gpio" name="opentherm[inGpio]" min="0" max="254" step="1">
</label>
<label for="opentherm-in-gpio">
Out GPIO Out GPIO
<input type="number" inputmode="numeric" class="opentherm-out-pin" name="opentherm[outPin]" min="0" max="99" step="1" required> <input type="number" inputmode="numeric" class="opentherm-out-gpio" name="opentherm[outGpio]" min="0" max="254" step="1">
</label> </label>
<label for="opentherm-member-id-code"> <label for="opentherm-member-id-code">
Master MemberID code Master MemberID code
@@ -77,10 +89,6 @@
</label> </label>
</div> </div>
<fieldset>
<mark>After changing GPIO, the ESP must be restarted for the changes to take effect.</mark>
</fieldset>
<fieldset> <fieldset>
<legend>Options</legend> <legend>Options</legend>
<label for="opentherm-dhw-present"> <label for="opentherm-dhw-present">
@@ -111,6 +119,10 @@
<input type="checkbox" class="opentherm-sync-modulation-with-heating" name="opentherm[modulationSyncWithHeating]" value="true"> <input type="checkbox" class="opentherm-sync-modulation-with-heating" name="opentherm[modulationSyncWithHeating]" value="true">
Sync modulation with heating Sync modulation with heating
</label> </label>
<label for="opentherm-get-min-max-temp">
<input type="checkbox" class="opentherm-get-min-max-temp" name="opentherm[getMinMaxTemp]" value="true">
Get min/max temp from boiler
</label>
</fieldset> </fieldset>
<button type="submit">Save</button> <button type="submit">Save</button>
@@ -190,9 +202,9 @@
</label> </label>
</fieldset> </fieldset>
<label for="outdoor-sensor-pin"> <label for="outdoor-sensor-gpio">
GPIO GPIO
<input type="number" inputmode="numeric" class="outdoor-sensor-pin" name="sensors[outdoor][pin]" min="0" max="99" step="1" required> <input type="number" inputmode="numeric" class="outdoor-sensor-gpio" name="sensors[outdoor][gpio]" min="0" max="254" step="1">
</label> </label>
<label for="outdoor-sensor-offset"> <label for="outdoor-sensor-offset">
Temp offset (calibration) Temp offset (calibration)
@@ -229,9 +241,9 @@
</label> </label>
</fieldset> </fieldset>
<label for="indoor-sensor-pin"> <label for="indoor-sensor-gpio">
GPIO GPIO
<input type="number" inputmode="numeric" class="indoor-sensor-pin" name="sensors[indoor][pin]" min="0" max="99" step="1" required> <input type="number" inputmode="numeric" class="indoor-sensor-gpio" name="sensors[indoor][gpio]" min="0" max="254" step="1">
</label> </label>
<div class="grid"> <div class="grid">
@@ -267,9 +279,9 @@
<br> <br>
<div class="grid"> <div class="grid">
<label for="extpump-pin"> <label for="extpump-gpio">
Relay GPIO Relay GPIO
<input type="number" inputmode="numeric" class="extpump-pin" name="externalPump[pin]" min="0" max="99" step="1" required> <input type="number" inputmode="numeric" class="extpump-gpio" name="externalPump[gpio]" min="0" max="254" step="1">
</label> </label>
<label for="extpump-pc-time"> <label for="extpump-pc-time">
Post circulation time <small>(min)</small> Post circulation time <small>(min)</small>
@@ -302,6 +314,18 @@
<div id="system-settings-busy" aria-busy="true"></div> <div id="system-settings-busy" aria-busy="true"></div>
<form action="/api/settings" id="system-settings" class="hidden"> <form action="/api/settings" id="system-settings" class="hidden">
<fieldset>
<legend>Unit system</legend>
<label>
<input type="radio" class="system-unit-system" name="system[unitSystem]" value="0" />
Metric (celsius)
</label>
<label>
<input type="radio" class="system-unit-system" name="system[unitSystem]" value="1" />
Imperial (fahrenheit)
</label>
</fieldset>
<fieldset> <fieldset>
<label for="system-debug"> <label for="system-debug">
<input type="checkbox" class="system-debug" name="system[debug]" value="true"> <input type="checkbox" class="system-debug" name="system[debug]" value="true">

Binary file not shown.

View File

@@ -8,6 +8,7 @@ public:
typedef std::function<void(unsigned long, unsigned long, OpenThermResponseStatus, byte)> AfterSendRequestCallback; typedef std::function<void(unsigned long, unsigned long, OpenThermResponseStatus, byte)> AfterSendRequestCallback;
CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false) : OpenTherm(inPin, outPin, isSlave) {} CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false) : OpenTherm(inPin, outPin, isSlave) {}
~CustomOpenTherm() {}
CustomOpenTherm* setYieldCallback(YieldCallback callback = nullptr) { CustomOpenTherm* setYieldCallback(YieldCallback callback = nullptr) {
this->yieldCallback = callback; this->yieldCallback = callback;
@@ -46,7 +47,7 @@ public:
unsigned long _response; unsigned long _response;
OpenThermResponseStatus _responseStatus = OpenThermResponseStatus::NONE; OpenThermResponseStatus _responseStatus = OpenThermResponseStatus::NONE;
if (!this->sendRequestAync(request)) { if (!this->sendRequestAsync(request)) {
_response = 0; _response = 0;
} else { } else {
@@ -88,7 +89,7 @@ public:
| (dhwBlocking << 6); | (dhwBlocking << 6);
data <<= 8; data <<= 8;
return this->sendRequest(this->buildRequest( return this->sendRequest(buildRequest(
OpenThermMessageType::READ_DATA, OpenThermMessageType::READ_DATA,
OpenThermMessageID::Status, OpenThermMessageID::Status,
data data
@@ -96,30 +97,30 @@ public:
} }
bool setHeatingCh1Temp(float temperature) { bool setHeatingCh1Temp(float temperature) {
unsigned long response = this->sendRequest(this->buildRequest( unsigned long response = this->sendRequest(buildRequest(
OpenThermMessageType::WRITE_DATA, OpenThermMessageType::WRITE_DATA,
OpenThermMessageID::TSet, OpenThermMessageID::TSet,
this->temperatureToData(temperature) temperatureToData(temperature)
)); ));
return isValidResponse(response); return isValidResponse(response);
} }
bool setHeatingCh2Temp(float temperature) { bool setHeatingCh2Temp(float temperature) {
unsigned long response = this->sendRequest(this->buildRequest( unsigned long response = this->sendRequest(buildRequest(
OpenThermMessageType::WRITE_DATA, OpenThermMessageType::WRITE_DATA,
OpenThermMessageID::TsetCH2, OpenThermMessageID::TsetCH2,
this->temperatureToData(temperature) temperatureToData(temperature)
)); ));
return isValidResponse(response); return isValidResponse(response);
} }
bool setDhwTemp(float temperature) { bool setDhwTemp(float temperature) {
unsigned long response = this->sendRequest(this->buildRequest( unsigned long response = this->sendRequest(buildRequest(
OpenThermMessageType::WRITE_DATA, OpenThermMessageType::WRITE_DATA,
OpenThermMessageID::TdhwSet, OpenThermMessageID::TdhwSet,
this->temperatureToData(temperature) temperatureToData(temperature)
)); ));
return isValidResponse(response); return isValidResponse(response);
@@ -128,9 +129,9 @@ public:
bool sendBoilerReset() { bool sendBoilerReset() {
unsigned int data = 1; unsigned int data = 1;
data <<= 8; data <<= 8;
unsigned long response = this->sendRequest(this->buildRequest( unsigned long response = this->sendRequest(buildRequest(
OpenThermMessageType::WRITE_DATA, OpenThermMessageType::WRITE_DATA,
OpenThermMessageID::Command, OpenThermMessageID::RemoteRequest,
data data
)); ));
@@ -140,9 +141,9 @@ public:
bool sendServiceReset() { bool sendServiceReset() {
unsigned int data = 10; unsigned int data = 10;
data <<= 8; data <<= 8;
unsigned long response = this->sendRequest(this->buildRequest( unsigned long response = this->sendRequest(buildRequest(
OpenThermMessageType::WRITE_DATA, OpenThermMessageType::WRITE_DATA,
OpenThermMessageID::Command, OpenThermMessageID::RemoteRequest,
data data
)); ));
@@ -152,9 +153,9 @@ public:
bool sendWaterFilling() { bool sendWaterFilling() {
unsigned int data = 2; unsigned int data = 2;
data <<= 8; data <<= 8;
unsigned long response = this->sendRequest(this->buildRequest( unsigned long response = this->sendRequest(buildRequest(
OpenThermMessageType::WRITE_DATA, OpenThermMessageType::WRITE_DATA,
OpenThermMessageID::Command, OpenThermMessageID::RemoteRequest,
data data
)); ));
@@ -162,24 +163,13 @@ public:
} }
// converters // converters
float fromF88(unsigned long response) { template <class T>
const byte valueLB = response & 0xFF; static unsigned int toFloat(const T val) {
const byte valueHB = (response >> 8) & 0xFF;
float value = (int8_t)valueHB;
return value + (float)valueLB / 256.0;
}
template <class T> unsigned int toF88(T val) {
return (unsigned int)(val * 256); return (unsigned int)(val * 256);
} }
int16_t fromS16(unsigned long response) { static short getInt(const unsigned long response) {
const byte valueLB = response & 0xFF; return response & 0xffff;
const byte valueHB = (response >> 8) & 0xFF;
int16_t value = valueHB;
return ((value << 8) + valueLB);
} }
protected: protected:

View File

@@ -33,6 +33,8 @@ const char HA_AVAILABILITY_MODE[] PROGMEM = "availability_mode";
const char HA_TOPIC[] PROGMEM = "topic"; const char HA_TOPIC[] PROGMEM = "topic";
const char HA_DEVICE_CLASS[] PROGMEM = "device_class"; const char HA_DEVICE_CLASS[] PROGMEM = "device_class";
const char HA_UNIT_OF_MEASUREMENT[] PROGMEM = "unit_of_measurement"; const char HA_UNIT_OF_MEASUREMENT[] PROGMEM = "unit_of_measurement";
const char HA_UNIT_OF_MEASUREMENT_C[] PROGMEM = "°C";
const char HA_UNIT_OF_MEASUREMENT_F[] PROGMEM = "°F";
const char HA_ICON[] PROGMEM = "icon"; const char HA_ICON[] PROGMEM = "icon";
const char HA_MIN[] PROGMEM = "min"; const char HA_MIN[] PROGMEM = "min";
const char HA_MAX[] PROGMEM = "max"; const char HA_MAX[] PROGMEM = "max";
@@ -50,6 +52,7 @@ const char HA_TEMPERATURE_COMMAND_TOPIC[] PROGMEM = "temperature_command_t
const char HA_TEMPERATURE_COMMAND_TEMPLATE[] PROGMEM = "temperature_command_template"; const char HA_TEMPERATURE_COMMAND_TEMPLATE[] PROGMEM = "temperature_command_template";
const char HA_TEMPERATURE_STATE_TOPIC[] PROGMEM = "temperature_state_topic"; const char HA_TEMPERATURE_STATE_TOPIC[] PROGMEM = "temperature_state_topic";
const char HA_TEMPERATURE_STATE_TEMPLATE[] PROGMEM = "temperature_state_template"; const char HA_TEMPERATURE_STATE_TEMPLATE[] PROGMEM = "temperature_state_template";
const char HA_TEMPERATURE_UNIT[] PROGMEM = "temperature_unit";
const char HA_MODE_COMMAND_TOPIC[] PROGMEM = "mode_command_topic"; const char HA_MODE_COMMAND_TOPIC[] PROGMEM = "mode_command_topic";
const char HA_MODE_COMMAND_TEMPLATE[] PROGMEM = "mode_command_template"; const char HA_MODE_COMMAND_TEMPLATE[] PROGMEM = "mode_command_template";
const char HA_MODE_STATE_TOPIC[] PROGMEM = "mode_state_topic"; const char HA_MODE_STATE_TOPIC[] PROGMEM = "mode_state_topic";

View File

@@ -67,7 +67,7 @@ namespace Network {
return this; return this;
} }
Manager* setStaticConfig(IPAddress &ip, IPAddress &gateway, IPAddress &subnet, IPAddress &dns) { Manager* setStaticConfig(IPAddress& ip, IPAddress& gateway, IPAddress& subnet, IPAddress& dns) {
this->staticIp = ip; this->staticIp = ip;
this->staticGateway = gateway; this->staticGateway = gateway;
this->staticSubnet = subnet; this->staticSubnet = subnet;
@@ -181,7 +181,12 @@ namespace Network {
wifi_station_dhcpc_set_maxtry(5); wifi_station_dhcpc_set_maxtry(5);
#endif #endif
#ifdef ARDUINO_ARCH_ESP32
// Nothing. Because memory leaks when turn off WiFi on ESP32, bug?
return true;
#else
return WiFi.mode(WIFI_OFF); return WiFi.mode(WIFI_OFF);
#endif
} }
void reconnect() { void reconnect() {

View File

@@ -174,7 +174,7 @@ public:
Log.serrorln( Log.serrorln(
FPSTR(L_PORTAL_OTA), FPSTR(L_PORTAL_OTA),
F("File '%s', on writing %d bytes: %s"), F("File '%s', on writing %d bytes: %s"),
upload.filename.c_str(), upload.totalSize, result->error upload.filename.c_str(), upload.totalSize, result->error.c_str()
); );
} else { } else {

View File

@@ -15,9 +15,9 @@ extra_configs = secrets.default.ini
[env] [env]
framework = arduino framework = arduino
lib_deps = lib_deps =
bblanchon/ArduinoJson@^7.0.3 bblanchon/ArduinoJson@^7.0.4
;ihormelnyk/OpenTherm Library@^1.1.4 ;ihormelnyk/OpenTherm Library@^1.1.5
https://github.com/Laxilef/opentherm_library/archive/refs/heads/fix_start_bit.zip https://github.com/Laxilef/opentherm_library/archive/refs/heads/fix_lambda.zip
arduino-libraries/ArduinoMqttClient@^0.1.8 arduino-libraries/ArduinoMqttClient@^0.1.8
lennarthennigs/ESP Telnet@^2.2 lennarthennigs/ESP Telnet@^2.2
gyverlibs/FileData@^1.0.2 gyverlibs/FileData@^1.0.2
@@ -30,44 +30,43 @@ build_flags =
-D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305 -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
-mtext-section-literals -mtext-section-literals
-D MQTT_CLIENT_STD_FUNCTION_CALLBACK=1 -D MQTT_CLIENT_STD_FUNCTION_CALLBACK=1
;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_PORT=Serial ;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_HTTP_SERVER -D DEBUG_ESP_PORT=Serial
-D USE_SERIAL=${secrets.use_serial} -D USE_SERIAL=${secrets.use_serial}
-D USE_TELNET=${secrets.use_telnet} -D USE_TELNET=${secrets.use_telnet}
-D DEBUG_BY_DEFAULT=${secrets.debug} -D DEBUG_BY_DEFAULT=${secrets.debug}
-D HOSTNAME_DEFAULT='"${secrets.hostname}"' -D DEFAULT_HOSTNAME='"${secrets.hostname}"'
-D AP_SSID_DEFAULT='"${secrets.ap_ssid}"' -D DEFAULT_AP_SSID='"${secrets.ap_ssid}"'
-D AP_PASSWORD_DEFAULT='"${secrets.ap_password}"' -D DEFAULT_AP_PASSWORD='"${secrets.ap_password}"'
-D STA_SSID_DEFAULT='"${secrets.sta_ssid}"' -D DEFAULT_STA_SSID='"${secrets.sta_ssid}"'
-D STA_PASSWORD_DEFAULT='"${secrets.sta_password}"' -D DEFAULT_STA_PASSWORD='"${secrets.sta_password}"'
-D PORTAL_LOGIN_DEFAULT='"${secrets.portal_login}"' -D DEFAULT_PORTAL_LOGIN='"${secrets.portal_login}"'
-D PORTAL_PASSWORD_DEFAULT='"${secrets.portal_password}"' -D DEFAULT_PORTAL_PASSWORD='"${secrets.portal_password}"'
-D MQTT_SERVER_DEFAULT='"${secrets.mqtt_server}"' -D DEFAULT_MQTT_SERVER='"${secrets.mqtt_server}"'
-D MQTT_PORT_DEFAULT=${secrets.mqtt_port} -D DEFAULT_MQTT_PORT=${secrets.mqtt_port}
-D MQTT_USER_DEFAULT='"${secrets.mqtt_user}"' -D DEFAULT_MQTT_USER='"${secrets.mqtt_user}"'
-D MQTT_PASSWORD_DEFAULT='"${secrets.mqtt_password}"' -D DEFAULT_MQTT_PASSWORD='"${secrets.mqtt_password}"'
-D MQTT_PREFIX_DEFAULT='"${secrets.mqtt_prefix}"' -D DEFAULT_MQTT_PREFIX='"${secrets.mqtt_prefix}"'
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = direct monitor_filters = direct
board_build.flash_mode = dio board_build.flash_mode = dio
board_build.filesystem = littlefs board_build.filesystem = littlefs
version = 1.4.0-rc.16 version = 1.4.0-rc.20
; Defaults ; Defaults
[esp8266_defaults] [esp8266_defaults]
platform = espressif8266 platform = espressif8266
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
nrwiersma/ESP8266Scheduler@^1.1 nrwiersma/ESP8266Scheduler@^1.2
lib_ignore = lib_ignore =
extra_scripts = extra_scripts =
post:tools/build.py post:tools/build.py
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
board_build.ldscript = eagle.flash.1m256.ld board_build.ldscript = eagle.flash.4m1m.ld
;board_build.ldscript = eagle.flash.4m1m.ld
[esp32_defaults] [esp32_defaults]
platform = espressif32 platform = espressif32@^6.6
board_build.partitions = esp32_partitions.csv board_build.partitions = esp32_partitions.csv
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
@@ -91,12 +90,12 @@ extra_scripts = ${esp8266_defaults.extra_scripts}
board_build.ldscript = ${esp8266_defaults.board_build.ldscript} board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
build_flags = build_flags =
${esp8266_defaults.build_flags} ${esp8266_defaults.build_flags}
-D OT_IN_PIN_DEFAULT=4 -D DEFAULT_OT_IN_GPIO=4
-D OT_OUT_PIN_DEFAULT=5 -D DEFAULT_OT_OUT_GPIO=5
-D SENSOR_OUTDOOR_PIN_DEFAULT=12 -D DEFAULT_SENSOR_OUTDOOR_GPIO=12
-D SENSOR_INDOOR_PIN_DEFAULT=14 -D DEFAULT_SENSOR_INDOOR_GPIO=14
-D LED_STATUS_PIN=13 -D LED_STATUS_GPIO=13
-D LED_OT_RX_PIN=15 -D LED_OT_RX_GPIO=15
[env:d1_mini_lite] [env:d1_mini_lite]
platform = ${esp8266_defaults.platform} platform = ${esp8266_defaults.platform}
@@ -107,12 +106,12 @@ extra_scripts = ${esp8266_defaults.extra_scripts}
board_build.ldscript = ${esp8266_defaults.board_build.ldscript} board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
build_flags = build_flags =
${esp8266_defaults.build_flags} ${esp8266_defaults.build_flags}
-D OT_IN_PIN_DEFAULT=4 -D DEFAULT_OT_IN_GPIO=4
-D OT_OUT_PIN_DEFAULT=5 -D DEFAULT_OT_OUT_GPIO=5
-D SENSOR_OUTDOOR_PIN_DEFAULT=12 -D DEFAULT_SENSOR_OUTDOOR_GPIO=12
-D SENSOR_INDOOR_PIN_DEFAULT=14 -D DEFAULT_SENSOR_INDOOR_GPIO=14
-D LED_STATUS_PIN=13 -D LED_STATUS_GPIO=13
-D LED_OT_RX_PIN=15 -D LED_OT_RX_GPIO=15
[env:d1_mini_pro] [env:d1_mini_pro]
platform = ${esp8266_defaults.platform} platform = ${esp8266_defaults.platform}
@@ -123,12 +122,12 @@ extra_scripts = ${esp8266_defaults.extra_scripts}
board_build.ldscript = ${esp8266_defaults.board_build.ldscript} board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
build_flags = build_flags =
${esp8266_defaults.build_flags} ${esp8266_defaults.build_flags}
-D OT_IN_PIN_DEFAULT=4 -D DEFAULT_OT_IN_GPIO=4
-D OT_OUT_PIN_DEFAULT=5 -D DEFAULT_OT_OUT_GPIO=5
-D SENSOR_OUTDOOR_PIN_DEFAULT=12 -D DEFAULT_SENSOR_OUTDOOR_GPIO=12
-D SENSOR_INDOOR_PIN_DEFAULT=14 -D DEFAULT_SENSOR_INDOOR_GPIO=14
-D LED_STATUS_PIN=13 -D LED_STATUS_GPIO=13
-D LED_OT_RX_PIN=15 -D LED_OT_RX_GPIO=15
[env:s2_mini] [env:s2_mini]
platform = ${esp32_defaults.platform} platform = ${esp32_defaults.platform}
@@ -139,12 +138,12 @@ lib_ignore = ${esp32_defaults.lib_ignore}
extra_scripts = ${esp32_defaults.extra_scripts} extra_scripts = ${esp32_defaults.extra_scripts}
build_flags = build_flags =
${esp32_defaults.build_flags} ${esp32_defaults.build_flags}
-D OT_IN_PIN_DEFAULT=33 -D DEFAULT_OT_IN_GPIO=33
-D OT_OUT_PIN_DEFAULT=35 -D DEFAULT_OT_OUT_GPIO=35
-D SENSOR_OUTDOOR_PIN_DEFAULT=9 -D DEFAULT_SENSOR_OUTDOOR_GPIO=9
-D SENSOR_INDOOR_PIN_DEFAULT=7 -D DEFAULT_SENSOR_INDOOR_GPIO=7
-D LED_STATUS_PIN=11 -D LED_STATUS_GPIO=11
-D LED_OT_RX_PIN=12 -D LED_OT_RX_GPIO=12
[env:s3_mini] [env:s3_mini]
platform = ${esp32_defaults.platform} platform = ${esp32_defaults.platform}
@@ -158,12 +157,12 @@ extra_scripts = ${esp32_defaults.extra_scripts}
build_flags = build_flags =
${esp32_defaults.build_flags} ${esp32_defaults.build_flags}
-D USE_BLE=1 -D USE_BLE=1
-D OT_IN_PIN_DEFAULT=35 -D DEFAULT_OT_IN_GPIO=35
-D OT_OUT_PIN_DEFAULT=36 -D DEFAULT_OT_OUT_GPIO=36
-D SENSOR_OUTDOOR_PIN_DEFAULT=13 -D DEFAULT_SENSOR_OUTDOOR_GPIO=13
-D SENSOR_INDOOR_PIN_DEFAULT=12 -D DEFAULT_SENSOR_INDOOR_GPIO=12
-D LED_STATUS_PIN=11 -D LED_STATUS_GPIO=11
-D LED_OT_RX_PIN=10 -D LED_OT_RX_GPIO=10
[env:c3_mini] [env:c3_mini]
platform = ${esp32_defaults.platform} platform = ${esp32_defaults.platform}
@@ -179,12 +178,12 @@ build_unflags =
build_flags = build_flags =
${esp32_defaults.build_flags} ${esp32_defaults.build_flags}
-D USE_BLE=1 -D USE_BLE=1
-D OT_IN_PIN_DEFAULT=8 -D DEFAULT_OT_IN_GPIO=8
-D OT_OUT_PIN_DEFAULT=10 -D DEFAULT_OT_OUT_GPIO=10
-D SENSOR_OUTDOOR_PIN_DEFAULT=0 -D DEFAULT_SENSOR_OUTDOOR_GPIO=0
-D SENSOR_INDOOR_PIN_DEFAULT=1 -D DEFAULT_SENSOR_INDOOR_GPIO=1
-D LED_STATUS_PIN=4 -D LED_STATUS_GPIO=4
-D LED_OT_RX_PIN=5 -D LED_OT_RX_GPIO=5
[env:nodemcu_32s] [env:nodemcu_32s]
platform = ${esp32_defaults.platform} platform = ${esp32_defaults.platform}
@@ -198,12 +197,12 @@ extra_scripts = ${esp32_defaults.extra_scripts}
build_flags = build_flags =
${esp32_defaults.build_flags} ${esp32_defaults.build_flags}
-D USE_BLE=1 -D USE_BLE=1
-D OT_IN_PIN_DEFAULT=21 -D DEFAULT_OT_IN_GPIO=21
-D OT_OUT_PIN_DEFAULT=22 -D DEFAULT_OT_OUT_GPIO=22
-D SENSOR_OUTDOOR_PIN_DEFAULT=12 -D DEFAULT_SENSOR_OUTDOOR_GPIO=12
-D SENSOR_INDOOR_PIN_DEFAULT=13 -D DEFAULT_SENSOR_INDOOR_GPIO=13
-D LED_STATUS_PIN=2 ; 18 -D LED_STATUS_GPIO=2 ; 18
-D LED_OT_RX_PIN=19 -D LED_OT_RX_GPIO=19
;-D WOKWI=1 ;-D WOKWI=1
[env:d1_mini32] [env:d1_mini32]
@@ -218,9 +217,9 @@ extra_scripts = ${esp32_defaults.extra_scripts}
build_flags = build_flags =
${esp32_defaults.build_flags} ${esp32_defaults.build_flags}
-D USE_BLE=1 -D USE_BLE=1
-D OT_IN_PIN_DEFAULT=21 -D DEFAULT_OT_IN_GPIO=21
-D OT_OUT_PIN_DEFAULT=22 -D DEFAULT_OT_OUT_GPIO=22
-D SENSOR_OUTDOOR_PIN_DEFAULT=12 -D DEFAULT_SENSOR_OUTDOOR_GPIO=12
-D SENSOR_INDOOR_PIN_DEFAULT=18 -D DEFAULT_SENSOR_INDOOR_GPIO=18
-D LED_STATUS_PIN=2 -D LED_STATUS_GPIO=2
-D LED_OT_RX_PIN=19 -D LED_OT_RX_GPIO=19

View File

@@ -27,22 +27,31 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SWITCH), F("emergency")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SWITCH), F("emergency")).c_str(), doc);
} }
bool publishNumberEmergencyTarget(bool enabledByDefault = true) { bool publishNumberEmergencyTarget(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("emergency_target")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("emergency_target"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("emergency_target")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("emergency_target"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
doc[FPSTR(HA_MIN)] = 5;
doc[FPSTR(HA_MAX)] = 50;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
doc[FPSTR(HA_MIN)] = 41;
doc[FPSTR(HA_MAX)] = 122;
}
doc[FPSTR(HA_NAME)] = F("Emergency target temp"); doc[FPSTR(HA_NAME)] = F("Emergency target temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-alert"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-alert");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.emergency.target|float(0)|round(1) }}"); doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.emergency.target|float(0)|round(1) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"emergency\": {\"target\" : {{ value }}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"emergency\": {\"target\" : {{ value }}}}");
doc[FPSTR(HA_MIN)] = 5;
doc[FPSTR(HA_MAX)] = 50;
doc[FPSTR(HA_STEP)] = 0.5; doc[FPSTR(HA_STEP)] = 0.5;
doc[FPSTR(HA_MODE)] = "box"; doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120; doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
@@ -142,7 +151,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SWITCH), F("heating_turbo")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SWITCH), F("heating_turbo")).c_str(), doc);
} }
bool publishNumberHeatingTarget(byte minTemp = 20, byte maxTemp = 90, bool enabledByDefault = true) { bool publishNumberHeatingTarget(UnitSystem unit = UnitSystem::METRIC, byte minTemp = 20, byte maxTemp = 90, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
@@ -150,7 +159,14 @@ public:
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_target")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_target"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Heating target"); doc[FPSTR(HA_NAME)] = F("Heating target");
doc[FPSTR(HA_ICON)] = F("mdi:radiator"); doc[FPSTR(HA_ICON)] = F("mdi:radiator");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
@@ -167,14 +183,21 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("heating_target")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("heating_target")).c_str(), doc);
} }
bool publishNumberHeatingHysteresis(bool enabledByDefault = true) { bool publishNumberHeatingHysteresis(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_hysteresis")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_hysteresis"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_hysteresis")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_hysteresis"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Heating hysteresis"); doc[FPSTR(HA_NAME)] = F("Heating hysteresis");
doc[FPSTR(HA_ICON)] = F("mdi:altimeter"); doc[FPSTR(HA_ICON)] = F("mdi:altimeter");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
@@ -191,7 +214,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("heating_hysteresis")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("heating_hysteresis")).c_str(), doc);
} }
bool publishSensorHeatingSetpoint(bool enabledByDefault = true) { bool publishSensorHeatingSetpoint(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
@@ -200,7 +223,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Heating setpoint"); doc[FPSTR(HA_NAME)] = F("Heating setpoint");
doc[FPSTR(HA_ICON)] = F("mdi:coolant-temperature"); doc[FPSTR(HA_ICON)] = F("mdi:coolant-temperature");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -211,7 +241,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_setpoint")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_setpoint")).c_str(), doc);
} }
bool publishSensorBoilerHeatingMinTemp(bool enabledByDefault = true) { bool publishSensorBoilerHeatingMinTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -223,7 +253,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Boiler heating min temp"); doc[FPSTR(HA_NAME)] = F("Boiler heating min temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -234,7 +271,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_heating_min_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_heating_min_temp")).c_str(), doc);
} }
bool publishSensorBoilerHeatingMaxTemp(bool enabledByDefault = true) { bool publishSensorBoilerHeatingMaxTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -246,7 +283,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Boiler heating max temp"); doc[FPSTR(HA_NAME)] = F("Boiler heating max temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -257,22 +301,31 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_heating_max_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_heating_max_temp")).c_str(), doc);
} }
bool publishNumberHeatingMinTemp(bool enabledByDefault = true) { bool publishNumberHeatingMinTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_min_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_min_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_min_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_min_temp"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
doc[FPSTR(HA_MIN)] = 0;
doc[FPSTR(HA_MAX)] = 99;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
doc[FPSTR(HA_MIN)] = 32;
doc[FPSTR(HA_MAX)] = 211;
}
doc[FPSTR(HA_NAME)] = F("Heating min temp"); doc[FPSTR(HA_NAME)] = F("Heating min temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.heating.minTemp|float(0)|round(1) }}"); doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.heating.minTemp|float(0)|round(1) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"heating\": {\"minTemp\" : {{ value }}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"heating\": {\"minTemp\" : {{ value }}}}");
doc[FPSTR(HA_MIN)] = 0;
doc[FPSTR(HA_MAX)] = 99;
doc[FPSTR(HA_STEP)] = 1; doc[FPSTR(HA_STEP)] = 1;
doc[FPSTR(HA_MODE)] = "box"; doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120; doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
@@ -281,22 +334,31 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("heating_min_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("heating_min_temp")).c_str(), doc);
} }
bool publishNumberHeatingMaxTemp(bool enabledByDefault = true) { bool publishNumberHeatingMaxTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_max_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_max_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_max_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_max_temp"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
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;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
doc[FPSTR(HA_MIN)] = 33;
doc[FPSTR(HA_MAX)] = 212;
}
doc[FPSTR(HA_NAME)] = F("Heating max temp"); doc[FPSTR(HA_NAME)] = F("Heating max temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.heating.maxTemp|float(0)|round(1) }}"); doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.heating.maxTemp|float(0)|round(1) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"heating\": {\"maxTemp\" : {{ value }}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"heating\": {\"maxTemp\" : {{ value }}}}");
doc[FPSTR(HA_MIN)] = 1;
doc[FPSTR(HA_MAX)] = 100;
doc[FPSTR(HA_STEP)] = 1; doc[FPSTR(HA_STEP)] = 1;
doc[FPSTR(HA_MODE)] = "box"; doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120; doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
@@ -352,7 +414,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SWITCH), F("dhw")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SWITCH), F("dhw")).c_str(), doc);
} }
bool publishNumberDhwTarget(byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) { bool publishNumberDhwTarget(UnitSystem unit = UnitSystem::METRIC, byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
@@ -360,7 +422,14 @@ public:
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_target")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_target"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("DHW target"); doc[FPSTR(HA_NAME)] = F("DHW target");
doc[FPSTR(HA_ICON)] = F("mdi:water-pump"); doc[FPSTR(HA_ICON)] = F("mdi:water-pump");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
@@ -377,7 +446,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("dhw_target")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("dhw_target")).c_str(), doc);
} }
bool publishSensorBoilerDhwMinTemp(bool enabledByDefault = true) { bool publishSensorBoilerDhwMinTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -389,7 +458,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Boiler DHW min temp"); doc[FPSTR(HA_NAME)] = F("Boiler DHW min temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -400,7 +476,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_dhw_min_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_dhw_min_temp")).c_str(), doc);
} }
bool publishSensorBoilerDhwMaxTemp(bool enabledByDefault = true) { bool publishSensorBoilerDhwMaxTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -412,7 +488,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Boiler DHW max temp"); doc[FPSTR(HA_NAME)] = F("Boiler DHW max temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -423,22 +506,31 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_dhw_max_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_dhw_max_temp")).c_str(), doc);
} }
bool publishNumberDhwMinTemp(bool enabledByDefault = true) { bool publishNumberDhwMinTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_min_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_min_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_min_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_min_temp"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
doc[FPSTR(HA_MIN)] = 0;
doc[FPSTR(HA_MAX)] = 99;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
doc[FPSTR(HA_MIN)] = 32;
doc[FPSTR(HA_MAX)] = 211;
}
doc[FPSTR(HA_NAME)] = F("DHW min temp"); doc[FPSTR(HA_NAME)] = F("DHW min temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.dhw.minTemp|float(0)|round(1) }}"); doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.dhw.minTemp|float(0)|round(1) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"dhw\": {\"minTemp\" : {{ value }}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"dhw\": {\"minTemp\" : {{ value }}}}");
doc[FPSTR(HA_MIN)] = 0;
doc[FPSTR(HA_MAX)] = 99;
doc[FPSTR(HA_STEP)] = 1; doc[FPSTR(HA_STEP)] = 1;
doc[FPSTR(HA_MODE)] = "box"; doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120; doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
@@ -447,22 +539,31 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("dhw_min_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("dhw_min_temp")).c_str(), doc);
} }
bool publishNumberDhwMaxTemp(bool enabledByDefault = true) { bool publishNumberDhwMaxTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_max_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_max_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_max_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_max_temp"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
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;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
doc[FPSTR(HA_MIN)] = 33;
doc[FPSTR(HA_MAX)] = 212;
}
doc[FPSTR(HA_NAME)] = F("DHW max temp"); doc[FPSTR(HA_NAME)] = F("DHW max temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.dhw.maxTemp|float(0)|round(1) }}"); doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.dhw.maxTemp|float(0)|round(1) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"dhw\": {\"maxTemp\" : {{ value }}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"dhw\": {\"maxTemp\" : {{ value }}}}");
doc[FPSTR(HA_MIN)] = 1;
doc[FPSTR(HA_MAX)] = 100;
doc[FPSTR(HA_STEP)] = 1; doc[FPSTR(HA_STEP)] = 1;
doc[FPSTR(HA_MODE)] = "box"; doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120; doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
@@ -579,22 +680,31 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("pid_dt")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("pid_dt")).c_str(), doc);
} }
bool publishNumberPidMinTemp(bool enabledByDefault = true) { bool publishNumberPidMinTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("pid_min_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("pid_min_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("pid_min_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("pid_min_temp"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
doc[FPSTR(HA_MIN)] = 0;
doc[FPSTR(HA_MAX)] = 99;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
doc[FPSTR(HA_MIN)] = 0;
doc[FPSTR(HA_MAX)] = 211;
}
doc[FPSTR(HA_NAME)] = F("PID min temp"); doc[FPSTR(HA_NAME)] = F("PID min temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.pid.minTemp|float(0)|round(1) }}"); doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.pid.minTemp|float(0)|round(1) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"pid\": {\"minTemp\" : {{ value }}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"pid\": {\"minTemp\" : {{ value }}}}");
doc[FPSTR(HA_MIN)] = 0;
doc[FPSTR(HA_MAX)] = 99;
doc[FPSTR(HA_STEP)] = 1; doc[FPSTR(HA_STEP)] = 1;
doc[FPSTR(HA_MODE)] = "box"; doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120; doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
@@ -603,22 +713,31 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("pid_min_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("pid_min_temp")).c_str(), doc);
} }
bool publishNumberPidMaxTemp(bool enabledByDefault = true) { bool publishNumberPidMaxTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("pid_max_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("pid_max_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("pid_max_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("pid_max_temp"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
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;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
doc[FPSTR(HA_MIN)] = 1;
doc[FPSTR(HA_MAX)] = 212;
}
doc[FPSTR(HA_NAME)] = F("PID max temp"); doc[FPSTR(HA_NAME)] = F("PID max temp");
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up"); doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.pid.maxTemp|float(0)|round(1) }}"); doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.pid.maxTemp|float(0)|round(1) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"pid\": {\"maxTemp\" : {{ value }}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"pid\": {\"maxTemp\" : {{ value }}}}");
doc[FPSTR(HA_MIN)] = 1;
doc[FPSTR(HA_MAX)] = 100;
doc[FPSTR(HA_STEP)] = 1; doc[FPSTR(HA_STEP)] = 1;
doc[FPSTR(HA_MODE)] = "box"; doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120; doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
@@ -980,7 +1099,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("modulation")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("modulation")).c_str(), doc);
} }
bool publishSensorPressure(bool enabledByDefault = true) { bool publishSensorPressure(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -992,7 +1111,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("pressure"); doc[FPSTR(HA_DEVICE_CLASS)] = F("pressure");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("bar");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("bar");
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("psi");
}
doc[FPSTR(HA_NAME)] = F("Pressure"); doc[FPSTR(HA_NAME)] = F("Pressure");
doc[FPSTR(HA_ICON)] = F("mdi:gauge"); doc[FPSTR(HA_ICON)] = F("mdi:gauge");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -1003,7 +1129,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("pressure")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("pressure")).c_str(), doc);
} }
bool publishSensorDhwFlowRate(bool enabledByDefault = true) { bool publishSensorDhwFlowRate(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -1015,7 +1141,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("volume_flow_rate"); doc[FPSTR(HA_DEVICE_CLASS)] = F("volume_flow_rate");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("L/min");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("L/min");
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("gal/min");
}
doc[FPSTR(HA_NAME)] = F("DHW flow rate"); doc[FPSTR(HA_NAME)] = F("DHW flow rate");
doc[FPSTR(HA_ICON)] = F("mdi:water-pump"); doc[FPSTR(HA_ICON)] = F("mdi:water-pump");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -1027,21 +1160,30 @@ public:
} }
bool publishNumberIndoorTemp(bool enabledByDefault = true) { bool publishNumberIndoorTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("indoor_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("indoor_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("indoor_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("indoor_temp"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
doc[FPSTR(HA_MIN)] = -99;
doc[FPSTR(HA_MAX)] = 99;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
doc[FPSTR(HA_MIN)] = -147;
doc[FPSTR(HA_MAX)] = 211;
}
doc[FPSTR(HA_NAME)] = F("Indoor temperature"); doc[FPSTR(HA_NAME)] = F("Indoor temperature");
doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer"); doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.indoor|float(0)|round(1) }}"); doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.indoor|float(0)|round(1) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("state/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("state/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"temperatures\": {\"indoor\":{{ value }}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"temperatures\": {\"indoor\":{{ value }}}}");
doc[FPSTR(HA_MIN)] = -99;
doc[FPSTR(HA_MAX)] = 99;
doc[FPSTR(HA_STEP)] = 0.01; doc[FPSTR(HA_STEP)] = 0.01;
doc[FPSTR(HA_MODE)] = "box"; doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120; doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
@@ -1050,7 +1192,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("indoor_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("indoor_temp")).c_str(), doc);
} }
bool publishSensorIndoorTemp(bool enabledByDefault = true) { bool publishSensorIndoorTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
@@ -1059,7 +1201,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Indoor temperature"); doc[FPSTR(HA_NAME)] = F("Indoor temperature");
doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer"); doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -1070,21 +1219,30 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("indoor_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("indoor_temp")).c_str(), doc);
} }
bool publishNumberOutdoorTemp(bool enabledByDefault = true) { bool publishNumberOutdoorTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("outdoor_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("outdoor_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("outdoor_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("outdoor_temp"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
doc[FPSTR(HA_MIN)] = -99;
doc[FPSTR(HA_MAX)] = 99;
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
doc[FPSTR(HA_MIN)] = -147;
doc[FPSTR(HA_MAX)] = 211;
}
doc[FPSTR(HA_NAME)] = F("Outdoor temperature"); doc[FPSTR(HA_NAME)] = F("Outdoor temperature");
doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer-outline"); doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer-outline");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.outdoor|float(0)|round(1) }}"); doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.outdoor|float(0)|round(1) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("state/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("state/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"temperatures\": {\"outdoor\":{{ value }}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"temperatures\": {\"outdoor\":{{ value }}}}");
doc[FPSTR(HA_MIN)] = -99;
doc[FPSTR(HA_MAX)] = 99;
doc[FPSTR(HA_STEP)] = 0.01; doc[FPSTR(HA_STEP)] = 0.01;
doc[FPSTR(HA_MODE)] = "box"; doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120; doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
@@ -1093,7 +1251,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("outdoor_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("outdoor_temp")).c_str(), doc);
} }
bool publishSensorOutdoorTemp(bool enabledByDefault = true) { bool publishSensorOutdoorTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
@@ -1102,7 +1260,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Outdoor temperature"); doc[FPSTR(HA_NAME)] = F("Outdoor temperature");
doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer-outline"); doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer-outline");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -1113,7 +1278,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("outdoor_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("outdoor_temp")).c_str(), doc);
} }
bool publishSensorHeatingTemp(bool enabledByDefault = true) { bool publishSensorHeatingTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -1125,7 +1290,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Heating temperature"); doc[FPSTR(HA_NAME)] = F("Heating temperature");
doc[FPSTR(HA_ICON)] = F("mdi:radiator"); doc[FPSTR(HA_ICON)] = F("mdi:radiator");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -1136,7 +1308,37 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_temp")).c_str(), doc);
} }
bool publishSensorDhwTemp(bool enabledByDefault = true) { bool publishSensorHeatingReturnTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_return_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_return_temp"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Heating return temperature");
doc[FPSTR(HA_ICON)] = F("mdi:radiator");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.heatingReturn|float(0)|round(2) }}");
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
doc.shrinkToFit();
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_return_temp")).c_str(), doc);
}
bool publishSensorDhwTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -1148,7 +1350,14 @@ public:
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("DHW temperature"); doc[FPSTR(HA_NAME)] = F("DHW temperature");
doc[FPSTR(HA_ICON)] = F("mdi:water-pump"); doc[FPSTR(HA_ICON)] = F("mdi:water-pump");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
@@ -1159,8 +1368,38 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("dhw_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("dhw_temp")).c_str(), doc);
} }
bool publishSensorExhaustTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("exhaust_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("exhaust_temp"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
bool publishClimateHeating(byte minTemp = 20, byte maxTemp = 90, byte currentTempSource = HaHelper::TEMP_SOURCE_HEATING, bool enabledByDefault = true) { if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
}
doc[FPSTR(HA_NAME)] = F("Exhaust temperature");
doc[FPSTR(HA_ICON)] = F("mdi:smoke");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.exhaust|float(0)|round(2) }}");
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
doc.shrinkToFit();
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("exhaust_temp")).c_str(), doc);
}
bool publishClimateHeating(UnitSystem unit = UnitSystem::METRIC, byte minTemp = 20, byte maxTemp = 90, byte currentTempSource = HaHelper::TEMP_SOURCE_HEATING, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
@@ -1186,6 +1425,13 @@ public:
doc[FPSTR(HA_TEMPERATURE_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_TEMPERATURE_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_TEMPERATURE_STATE_TEMPLATE)] = F("{{ value_json.heating.target|float(0)|round(1) }}"); doc[FPSTR(HA_TEMPERATURE_STATE_TEMPLATE)] = F("{{ value_json.heating.target|float(0)|round(1) }}");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_TEMPERATURE_UNIT)] = "C";
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_TEMPERATURE_UNIT)] = "F";
}
doc[FPSTR(HA_MODE_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set")); doc[FPSTR(HA_MODE_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_MODE_COMMAND_TEMPLATE)] = F("{% if value == 'heat' %}{\"heating\": {\"enable\" : true}}" doc[FPSTR(HA_MODE_COMMAND_TEMPLATE)] = F("{% if value == 'heat' %}{\"heating\": {\"enable\" : true}}"
"{% elif value == 'off' %}{\"heating\": {\"enable\" : false}}{% endif %}"); "{% elif value == 'off' %}{\"heating\": {\"enable\" : false}}{% endif %}");
@@ -1213,7 +1459,7 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_CLIMATE), F("heating"), '_').c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_CLIMATE), F("heating"), '_').c_str(), doc);
} }
bool publishClimateDhw(byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) { bool publishClimateDhw(UnitSystem unit = UnitSystem::METRIC, byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
@@ -1231,6 +1477,13 @@ public:
doc[FPSTR(HA_TEMPERATURE_STATE_TOPIC)] = this->getDeviceTopic(F("settings")); doc[FPSTR(HA_TEMPERATURE_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_TEMPERATURE_STATE_TEMPLATE)] = F("{{ value_json.dhw.target|int(0) }}"); doc[FPSTR(HA_TEMPERATURE_STATE_TEMPLATE)] = F("{{ value_json.dhw.target|int(0) }}");
if (unit == UnitSystem::METRIC) {
doc[FPSTR(HA_TEMPERATURE_UNIT)] = "C";
} else if (unit == UnitSystem::IMPERIAL) {
doc[FPSTR(HA_TEMPERATURE_UNIT)] = "F";
}
doc[FPSTR(HA_MODE_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set")); doc[FPSTR(HA_MODE_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_MODE_COMMAND_TEMPLATE)] = F("{% if value == 'heat' %}{\"dhw\": {\"enable\" : true}}" doc[FPSTR(HA_MODE_COMMAND_TEMPLATE)] = F("{% if value == 'heat' %}{\"dhw\": {\"enable\" : true}}"
"{% elif value == 'off' %}{\"dhw\": {\"enable\" : false}}{% endif %}"); "{% elif value == 'off' %}{\"dhw\": {\"enable\" : false}}{% endif %}");

View File

@@ -52,14 +52,14 @@ protected:
} }
void setup() { void setup() {
#ifdef LED_STATUS_PIN #ifdef LED_STATUS_GPIO
pinMode(LED_STATUS_PIN, OUTPUT); pinMode(LED_STATUS_GPIO, OUTPUT);
digitalWrite(LED_STATUS_PIN, LOW); digitalWrite(LED_STATUS_GPIO, LOW);
#endif #endif
if (settings.externalPump.pin != 0) { if (GPIO_IS_VALID(settings.externalPump.gpio)) {
pinMode(settings.externalPump.pin, OUTPUT); pinMode(settings.externalPump.gpio, OUTPUT);
digitalWrite(settings.externalPump.pin, LOW); digitalWrite(settings.externalPump.gpio, LOW);
} }
} }
@@ -89,10 +89,6 @@ protected:
Log.sinfoln(FPSTR(L_MAIN), F("Restart signal received. Restart after 10 sec.")); Log.sinfoln(FPSTR(L_MAIN), F("Restart signal received. Restart after 10 sec."));
} }
if (!tOt->isEnabled() && settings.opentherm.inPin > 0 && settings.opentherm.outPin > 0 && settings.opentherm.inPin != settings.opentherm.outPin) {
tOt->enable();
}
if (network->isConnected()) { if (network->isConnected()) {
vars.sensors.rssi = WiFi.RSSI(); vars.sensors.rssi = WiFi.RSSI();
@@ -140,8 +136,8 @@ protected:
this->yield(); this->yield();
#ifdef LED_STATUS_PIN #ifdef LED_STATUS_GPIO
this->ledStatus(LED_STATUS_PIN); this->ledStatus(LED_STATUS_GPIO);
#endif #endif
this->externalPump(); this->externalPump();
this->yield(); this->yield();
@@ -211,7 +207,7 @@ protected:
} }
} }
void ledStatus(uint8_t ledPin) { void ledStatus(uint8_t gpio) {
uint8_t errors[4]; uint8_t errors[4];
uint8_t errCount = 0; uint8_t errCount = 0;
static uint8_t errPos = 0; static uint8_t errPos = 0;
@@ -219,7 +215,7 @@ protected:
static bool ledOn = false; static bool ledOn = false;
if (!this->blinkerInitialized) { if (!this->blinkerInitialized) {
this->blinker->init(ledPin); this->blinker->init(gpio);
this->blinkerInitialized = true; this->blinkerInitialized = true;
} }
@@ -247,14 +243,14 @@ protected:
if (!this->blinker->running() && millis() - endBlinkTime >= 5000) { if (!this->blinker->running() && millis() - endBlinkTime >= 5000) {
if (errCount == 0) { if (errCount == 0) {
if (!ledOn) { if (!ledOn) {
digitalWrite(ledPin, HIGH); digitalWrite(gpio, HIGH);
ledOn = true; ledOn = true;
} }
return; return;
} else if (ledOn) { } else if (ledOn) {
digitalWrite(ledPin, LOW); digitalWrite(gpio, LOW);
ledOn = false; ledOn = false;
endBlinkTime = millis(); endBlinkTime = millis();
return; return;
@@ -283,10 +279,10 @@ protected:
this->heatingEnabled = true; this->heatingEnabled = true;
} }
if (!settings.externalPump.use || settings.externalPump.pin == 0) { if (!settings.externalPump.use || !GPIO_IS_VALID(settings.externalPump.gpio)) {
if (vars.states.externalPump) { if (vars.states.externalPump) {
if (settings.externalPump.pin != 0) { if (GPIO_IS_VALID(settings.externalPump.gpio)) {
digitalWrite(settings.externalPump.pin, LOW); digitalWrite(settings.externalPump.gpio, LOW);
} }
vars.states.externalPump = false; vars.states.externalPump = false;
@@ -300,7 +296,7 @@ protected:
if (vars.states.externalPump && !this->heatingEnabled) { if (vars.states.externalPump && !this->heatingEnabled) {
if (this->extPumpStartReason == MainTask::PumpStartReason::HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) { if (this->extPumpStartReason == MainTask::PumpStartReason::HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) {
digitalWrite(settings.externalPump.pin, LOW); digitalWrite(settings.externalPump.gpio, LOW);
vars.states.externalPump = false; vars.states.externalPump = false;
vars.parameters.extPumpLastEnableTime = millis(); vars.parameters.extPumpLastEnableTime = millis();
@@ -308,7 +304,7 @@ protected:
Log.sinfoln("EXTPUMP", F("Disabled: expired post circulation time")); Log.sinfoln("EXTPUMP", F("Disabled: expired post circulation time"));
} else if (this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK && millis() - this->externalPumpStartTime >= (settings.externalPump.antiStuckTime * 1000u)) { } else if (this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK && millis() - this->externalPumpStartTime >= (settings.externalPump.antiStuckTime * 1000u)) {
digitalWrite(settings.externalPump.pin, LOW); digitalWrite(settings.externalPump.gpio, LOW);
vars.states.externalPump = false; vars.states.externalPump = false;
vars.parameters.extPumpLastEnableTime = millis(); vars.parameters.extPumpLastEnableTime = millis();
@@ -324,7 +320,7 @@ protected:
this->externalPumpStartTime = millis(); this->externalPumpStartTime = millis();
this->extPumpStartReason = MainTask::PumpStartReason::HEATING; this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
digitalWrite(settings.externalPump.pin, HIGH); digitalWrite(settings.externalPump.gpio, HIGH);
Log.sinfoln("EXTPUMP", F("Enabled: heating on")); Log.sinfoln("EXTPUMP", F("Enabled: heating on"));
@@ -333,7 +329,7 @@ protected:
this->externalPumpStartTime = millis(); this->externalPumpStartTime = millis();
this->extPumpStartReason = MainTask::PumpStartReason::ANTISTUCK; this->extPumpStartReason = MainTask::PumpStartReason::ANTISTUCK;
digitalWrite(settings.externalPump.pin, HIGH); digitalWrite(settings.externalPump.gpio, HIGH);
Log.sinfoln("EXTPUMP", F("Enabled: anti stuck")); Log.sinfoln("EXTPUMP", F("Enabled: anti stuck"));
} }

View File

@@ -51,6 +51,7 @@ protected:
MqttClient* client = nullptr; MqttClient* client = nullptr;
HaHelper* haHelper = nullptr; HaHelper* haHelper = nullptr;
MqttWriter* writer = nullptr; MqttWriter* writer = nullptr;
UnitSystem currentUnitSystem = UnitSystem::METRIC;
unsigned short readyForSendTime = 15000; unsigned short readyForSendTime = 15000;
unsigned long lastReconnectTime = 0; unsigned long lastReconnectTime = 0;
unsigned long connectedTime = 0; unsigned long connectedTime = 0;
@@ -69,7 +70,7 @@ protected:
}*/ }*/
int getTaskPriority() { int getTaskPriority() {
return 1; return 2;
} }
bool isReadyForSend() { bool isReadyForSend() {
@@ -213,10 +214,11 @@ protected:
} }
// publish ha entities if not published // publish ha entities if not published
if (this->newConnection) { if (this->newConnection || this->currentUnitSystem != settings.system.unitSystem) {
this->publishHaEntities(); this->publishHaEntities();
this->publishNonStaticHaEntities(true); this->publishNonStaticHaEntities(true);
this->newConnection = false; this->newConnection = false;
this->currentUnitSystem = settings.system.unitSystem;
} else { } else {
// publish non static ha entities // publish non static ha entities
@@ -318,19 +320,19 @@ protected:
void publishHaEntities() { void publishHaEntities() {
// emergency // emergency
this->haHelper->publishSwitchEmergency(); this->haHelper->publishSwitchEmergency();
this->haHelper->publishNumberEmergencyTarget(); this->haHelper->publishNumberEmergencyTarget(settings.system.unitSystem);
this->haHelper->publishSwitchEmergencyUseEquitherm(); this->haHelper->publishSwitchEmergencyUseEquitherm();
this->haHelper->publishSwitchEmergencyUsePid(); this->haHelper->publishSwitchEmergencyUsePid();
// heating // heating
this->haHelper->publishSwitchHeating(false); this->haHelper->publishSwitchHeating(false);
this->haHelper->publishSwitchHeatingTurbo(); this->haHelper->publishSwitchHeatingTurbo();
this->haHelper->publishNumberHeatingHysteresis(); this->haHelper->publishNumberHeatingHysteresis(settings.system.unitSystem);
this->haHelper->publishSensorHeatingSetpoint(false); this->haHelper->publishSensorHeatingSetpoint(settings.system.unitSystem, false);
this->haHelper->publishSensorBoilerHeatingMinTemp(false); this->haHelper->publishSensorBoilerHeatingMinTemp(settings.system.unitSystem, false);
this->haHelper->publishSensorBoilerHeatingMaxTemp(false); this->haHelper->publishSensorBoilerHeatingMaxTemp(settings.system.unitSystem, false);
this->haHelper->publishNumberHeatingMinTemp(false); this->haHelper->publishNumberHeatingMinTemp(settings.system.unitSystem, false);
this->haHelper->publishNumberHeatingMaxTemp(false); this->haHelper->publishNumberHeatingMaxTemp(settings.system.unitSystem, false);
this->haHelper->publishNumberHeatingMaxModulation(false); this->haHelper->publishNumberHeatingMaxModulation(false);
// pid // pid
@@ -339,8 +341,8 @@ protected:
this->haHelper->publishNumberPidFactorI(); this->haHelper->publishNumberPidFactorI();
this->haHelper->publishNumberPidFactorD(); this->haHelper->publishNumberPidFactorD();
this->haHelper->publishNumberPidDt(false); this->haHelper->publishNumberPidDt(false);
this->haHelper->publishNumberPidMinTemp(false); this->haHelper->publishNumberPidMinTemp(settings.system.unitSystem, false);
this->haHelper->publishNumberPidMaxTemp(false); this->haHelper->publishNumberPidMaxTemp(settings.system.unitSystem, false);
// equitherm // equitherm
this->haHelper->publishSwitchEquitherm(); this->haHelper->publishSwitchEquitherm();
@@ -362,14 +364,16 @@ protected:
// sensors // sensors
this->haHelper->publishSensorModulation(false); this->haHelper->publishSensorModulation(false);
this->haHelper->publishSensorPressure(false); this->haHelper->publishSensorPressure(settings.system.unitSystem, false);
this->haHelper->publishSensorFaultCode(); this->haHelper->publishSensorFaultCode();
this->haHelper->publishSensorRssi(false); this->haHelper->publishSensorRssi(false);
this->haHelper->publishSensorUptime(false); this->haHelper->publishSensorUptime(false);
// temperatures // temperatures
this->haHelper->publishNumberIndoorTemp(); this->haHelper->publishNumberIndoorTemp(settings.system.unitSystem);
this->haHelper->publishSensorHeatingTemp(); this->haHelper->publishSensorHeatingTemp(settings.system.unitSystem);
this->haHelper->publishSensorHeatingReturnTemp(settings.system.unitSystem, false);
this->haHelper->publishSensorExhaustTemp(settings.system.unitSystem, false);
// buttons // buttons
this->haHelper->publishButtonRestart(false); this->haHelper->publishButtonRestart(false);
@@ -383,23 +387,36 @@ protected:
bool published = false; bool published = false;
bool isStupidMode = !settings.pid.enable && !settings.equitherm.enable; bool isStupidMode = !settings.pid.enable && !settings.equitherm.enable;
byte heatingMinTemp = isStupidMode ? settings.heating.minTemp : 10; byte heatingMinTemp = 0;
byte heatingMaxTemp = isStupidMode ? settings.heating.maxTemp : 30; byte heatingMaxTemp = 0;
bool editableOutdoorTemp = settings.sensors.outdoor.type == 1; bool editableOutdoorTemp = settings.sensors.outdoor.type == SensorType::MANUAL;
bool editableIndoorTemp = settings.sensors.indoor.type == 1; bool editableIndoorTemp = settings.sensors.indoor.type == SensorType::MANUAL;
if (isStupidMode) {
heatingMinTemp = settings.heating.minTemp;
heatingMaxTemp = settings.heating.maxTemp;
} else if (settings.system.unitSystem == UnitSystem::METRIC) {
heatingMinTemp = 5;
heatingMaxTemp = 30;
} else if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
heatingMinTemp = 41;
heatingMaxTemp = 86;
}
if (force || _dhwPresent != settings.opentherm.dhwPresent) { if (force || _dhwPresent != settings.opentherm.dhwPresent) {
_dhwPresent = settings.opentherm.dhwPresent; _dhwPresent = settings.opentherm.dhwPresent;
if (_dhwPresent) { if (_dhwPresent) {
this->haHelper->publishSwitchDhw(false); this->haHelper->publishSwitchDhw(false);
this->haHelper->publishSensorBoilerDhwMinTemp(false); this->haHelper->publishSensorBoilerDhwMinTemp(settings.system.unitSystem, false);
this->haHelper->publishSensorBoilerDhwMaxTemp(false); this->haHelper->publishSensorBoilerDhwMaxTemp(settings.system.unitSystem, false);
this->haHelper->publishNumberDhwMinTemp(false); this->haHelper->publishNumberDhwMinTemp(settings.system.unitSystem, false);
this->haHelper->publishNumberDhwMaxTemp(false); this->haHelper->publishNumberDhwMaxTemp(settings.system.unitSystem, false);
this->haHelper->publishBinSensorDhw(); this->haHelper->publishBinSensorDhw();
this->haHelper->publishSensorDhwTemp(); this->haHelper->publishSensorDhwTemp(settings.system.unitSystem);
this->haHelper->publishSensorDhwFlowRate(false); this->haHelper->publishSensorDhwFlowRate(settings.system.unitSystem, false);
} else { } else {
this->haHelper->deleteSwitchDhw(); this->haHelper->deleteSwitchDhw();
@@ -426,8 +443,9 @@ protected:
_heatingMaxTemp = heatingMaxTemp; _heatingMaxTemp = heatingMaxTemp;
_isStupidMode = isStupidMode; _isStupidMode = isStupidMode;
this->haHelper->publishNumberHeatingTarget(heatingMinTemp, heatingMaxTemp, false); this->haHelper->publishNumberHeatingTarget(settings.system.unitSystem, heatingMinTemp, heatingMaxTemp, false);
this->haHelper->publishClimateHeating( this->haHelper->publishClimateHeating(
settings.system.unitSystem,
heatingMinTemp, heatingMinTemp,
heatingMaxTemp, heatingMaxTemp,
isStupidMode ? HaHelper::TEMP_SOURCE_HEATING : HaHelper::TEMP_SOURCE_INDOOR isStupidMode ? HaHelper::TEMP_SOURCE_HEATING : HaHelper::TEMP_SOURCE_INDOOR
@@ -438,6 +456,7 @@ protected:
} else if (_isStupidMode != isStupidMode) { } else if (_isStupidMode != isStupidMode) {
_isStupidMode = isStupidMode; _isStupidMode = isStupidMode;
this->haHelper->publishClimateHeating( this->haHelper->publishClimateHeating(
settings.system.unitSystem,
heatingMinTemp, heatingMinTemp,
heatingMaxTemp, heatingMaxTemp,
isStupidMode ? HaHelper::TEMP_SOURCE_HEATING : HaHelper::TEMP_SOURCE_INDOOR isStupidMode ? HaHelper::TEMP_SOURCE_HEATING : HaHelper::TEMP_SOURCE_INDOOR
@@ -450,8 +469,8 @@ protected:
_dhwMinTemp = settings.dhw.minTemp; _dhwMinTemp = settings.dhw.minTemp;
_dhwMaxTemp = settings.dhw.maxTemp; _dhwMaxTemp = settings.dhw.maxTemp;
this->haHelper->publishNumberDhwTarget(settings.dhw.minTemp, settings.dhw.maxTemp, false); this->haHelper->publishNumberDhwTarget(settings.system.unitSystem, settings.dhw.minTemp, settings.dhw.maxTemp, false);
this->haHelper->publishClimateDhw(settings.dhw.minTemp, settings.dhw.maxTemp); this->haHelper->publishClimateDhw(settings.system.unitSystem, settings.dhw.minTemp, settings.dhw.maxTemp);
published = true; published = true;
} }
@@ -461,10 +480,10 @@ protected:
if (editableOutdoorTemp) { if (editableOutdoorTemp) {
this->haHelper->deleteSensorOutdoorTemp(); this->haHelper->deleteSensorOutdoorTemp();
this->haHelper->publishNumberOutdoorTemp(); this->haHelper->publishNumberOutdoorTemp(settings.system.unitSystem);
} else { } else {
this->haHelper->deleteNumberOutdoorTemp(); this->haHelper->deleteNumberOutdoorTemp();
this->haHelper->publishSensorOutdoorTemp(); this->haHelper->publishSensorOutdoorTemp(settings.system.unitSystem);
} }
published = true; published = true;
@@ -475,10 +494,10 @@ protected:
if (editableIndoorTemp) { if (editableIndoorTemp) {
this->haHelper->deleteSensorIndoorTemp(); this->haHelper->deleteSensorIndoorTemp();
this->haHelper->publishNumberIndoorTemp(); this->haHelper->publishNumberIndoorTemp(settings.system.unitSystem);
} else { } else {
this->haHelper->deleteNumberIndoorTemp(); this->haHelper->deleteNumberIndoorTemp();
this->haHelper->publishSensorIndoorTemp(); this->haHelper->publishSensorIndoorTemp(settings.system.unitSystem);
} }
published = true; published = true;

View File

@@ -1,31 +1,35 @@
#include <CustomOpenTherm.h> #include <CustomOpenTherm.h>
CustomOpenTherm* ot;
extern FileData fsSettings; extern FileData fsSettings;
class OpenThermTask : public Task { class OpenThermTask : public Task {
public: public:
OpenThermTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) { OpenThermTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
ot = new CustomOpenTherm(settings.opentherm.inPin, settings.opentherm.outPin);
}
static void IRAM_ATTR handleInterrupt() { ~OpenThermTask() {
ot->handleInterrupt(); delete this->instance;
} }
protected: protected:
unsigned short readyTime = 60000; const unsigned short readyTime = 60000;
unsigned short dhwSetTempInterval = 60000; const unsigned short dhwSetTempInterval = 60000;
unsigned short heatingSetTempInterval = 60000; const unsigned short heatingSetTempInterval = 60000;
const unsigned int initializingInterval = 3600000;
CustomOpenTherm* instance = nullptr;
unsigned long instanceCreatedTime = 0;
byte instanceInGpio = 0;
byte instanceOutGpio = 0;
bool isInitialized = false;
unsigned long initializedTime = 0;
unsigned int initializedMemberIdCode = 0;
byte dhwFlowRateMultiplier = 1;
byte pressureMultiplier = 1;
bool pump = true; bool pump = true;
unsigned long lastSuccessResponse = 0; unsigned long lastSuccessResponse = 0;
unsigned long prevUpdateNonEssentialVars = 0; unsigned long prevUpdateNonEssentialVars = 0;
unsigned long startupTime = millis();
unsigned long dhwSetTempTime = 0; unsigned long dhwSetTempTime = 0;
unsigned long heatingSetTempTime = 0; unsigned long heatingSetTempTime = 0;
byte dhwFlowRateMultiplier = 1;
byte pressureMultiplier = 1;
const char* getTaskName() { const char* getTaskName() {
return "OpenTherm"; return "OpenTherm";
@@ -36,85 +40,88 @@ protected:
} }
int getTaskPriority() { int getTaskPriority() {
return 2; return 5;
} }
void setup() { void setup() {
Log.sinfoln(FPSTR(L_OT), F("Started. GPIO IN: %hhu, GPIO OUT: %hhu"), settings.opentherm.inPin, settings.opentherm.outPin); if (settings.system.unitSystem != UnitSystem::METRIC) {
vars.parameters.heatingMinTemp = convertTemp(vars.parameters.heatingMinTemp, UnitSystem::METRIC, settings.system.unitSystem);
vars.parameters.heatingMaxTemp = convertTemp(vars.parameters.heatingMaxTemp, UnitSystem::METRIC, settings.system.unitSystem);
vars.parameters.dhwMinTemp = convertTemp(vars.parameters.dhwMinTemp, UnitSystem::METRIC, settings.system.unitSystem);
vars.parameters.dhwMaxTemp = convertTemp(vars.parameters.dhwMaxTemp, UnitSystem::METRIC, settings.system.unitSystem);
}
ot->setAfterSendRequestCallback([this](unsigned long request, unsigned long response, OpenThermResponseStatus status, byte attempt) { #ifdef LED_OT_RX_GPIO
pinMode(LED_OT_RX_GPIO, OUTPUT);
digitalWrite(LED_OT_RX_GPIO, LOW);
#endif
// delete instance
if (this->instance != nullptr) {
delete this->instance;
this->instance = nullptr;
Log.sinfoln(FPSTR(L_OT), F("Stopped"));
}
if (!GPIO_IS_VALID(settings.opentherm.inGpio) || !GPIO_IS_VALID(settings.opentherm.outGpio)) {
Log.swarningln(FPSTR(L_OT), F("Not started. GPIO IN: %hhu or GPIO OUT: %hhu is not valid"), settings.opentherm.inGpio, settings.opentherm.outGpio);
return;
}
// create instance
this->instance = new CustomOpenTherm(settings.opentherm.inGpio, settings.opentherm.outGpio);
// flags
this->instanceCreatedTime = millis();
this->instanceInGpio = settings.opentherm.inGpio;
this->instanceOutGpio = settings.opentherm.outGpio;
this->isInitialized = false;
Log.sinfoln(FPSTR(L_OT), F("Started. GPIO IN: %hhu, GPIO OUT: %hhu"), settings.opentherm.inGpio, settings.opentherm.outGpio);
this->instance->setAfterSendRequestCallback([this](unsigned long request, unsigned long response, OpenThermResponseStatus status, byte attempt) {
Log.straceln( Log.straceln(
FPSTR(L_OT), FPSTR(L_OT),
F("ID: %4d Request: %8lx Response: %8lx Attempt: %2d Status: %s"), F("ID: %4d Request: %8lx Response: %8lx Attempt: %2d Status: %s"),
ot->getDataID(request), request, response, attempt, ot->statusToString(status) CustomOpenTherm::getDataID(request), request, response, attempt, CustomOpenTherm::statusToString(status)
); );
if (status == OpenThermResponseStatus::SUCCESS) { if (status == OpenThermResponseStatus::SUCCESS) {
this->lastSuccessResponse = millis(); this->lastSuccessResponse = millis();
#ifdef LED_OT_RX_PIN #ifdef LED_OT_RX_GPIO
{ {
digitalWrite(LED_OT_RX_PIN, HIGH); digitalWrite(LED_OT_RX_GPIO, HIGH);
delayMicroseconds(2000); delayMicroseconds(2000);
digitalWrite(LED_OT_RX_PIN, LOW); digitalWrite(LED_OT_RX_GPIO, LOW);
} }
#endif #endif
} }
}); });
ot->setYieldCallback([this]() { this->instance->setYieldCallback([this]() {
this->delay(25); this->delay(25);
}); });
ot->setMinWaitTimeForStartBit(20000); this->instance->begin();
ot->begin(OpenThermTask::handleInterrupt);
#ifdef LED_OT_RX_PIN
pinMode(LED_OT_RX_PIN, OUTPUT);
digitalWrite(LED_OT_RX_PIN, LOW);
#endif
}
void initBoiler() {
this->dhwFlowRateMultiplier = 1;
this->pressureMultiplier = 1;
// Not all boilers support these, only try once when the boiler becomes connected
if (updateSlaveVersion()) {
Log.straceln(FPSTR(L_OT), F("Slave version: %u, type: %u"), vars.parameters.slaveVersion, vars.parameters.slaveType);
} else {
Log.swarningln(FPSTR(L_OT), F("Get slave version failed"));
}
// 0x013F
if (setMasterVersion(0x3F, 0x01)) {
Log.straceln(FPSTR(L_OT), F("Master version: %u, type: %u"), vars.parameters.masterVersion, vars.parameters.masterType);
} else {
Log.swarningln(FPSTR(L_OT), F("Set master version failed"));
}
if (updateSlaveConfig()) {
Log.straceln(FPSTR(L_OT), F("Slave member id: %u, flags: %u"), vars.parameters.slaveMemberId, vars.parameters.slaveFlags);
} else {
Log.swarningln(FPSTR(L_OT), F("Get slave config failed"));
}
if (setMasterConfig(settings.opentherm.memberIdCode & 0xFF, (settings.opentherm.memberIdCode & 0xFFFF) >> 8)) {
Log.straceln(FPSTR(L_OT), F("Master member id: %u, flags: %u"), vars.parameters.masterMemberId, vars.parameters.masterFlags);
} else {
Log.swarningln(FPSTR(L_OT), F("Set master config failed"));
}
} }
void loop() { void loop() {
static byte currentHeatingTemp, currentDhwTemp = 0; static byte currentHeatingTemp, currentDhwTemp = 0;
unsigned long localResponse;
bool heatingEnabled = (vars.states.emergency || settings.heating.enable) && this->pump && isReady(); if (this->instanceInGpio != settings.opentherm.inGpio || this->instanceOutGpio != settings.opentherm.outGpio) {
this->setup();
} else if (this->initializedMemberIdCode != settings.opentherm.memberIdCode || millis() - this->initializedTime > this->initializingInterval) {
this->isInitialized = false;
}
if (this->instance == nullptr) {
this->delay(5000);
return;
}
bool heatingEnabled = (vars.states.emergency || settings.heating.enable) && this->pump && this->isReady();
bool heatingCh2Enabled = settings.opentherm.heatingCh2Enabled; bool heatingCh2Enabled = settings.opentherm.heatingCh2Enabled;
if (settings.opentherm.heatingCh1ToCh2) { if (settings.opentherm.heatingCh1ToCh2) {
heatingCh2Enabled = heatingEnabled; heatingCh2Enabled = heatingEnabled;
@@ -123,7 +130,7 @@ protected:
heatingCh2Enabled = settings.opentherm.dhwPresent && settings.dhw.enable; heatingCh2Enabled = settings.opentherm.dhwPresent && settings.dhw.enable;
} }
localResponse = ot->setBoilerStatus( unsigned long response = this->instance->setBoilerStatus(
heatingEnabled, heatingEnabled,
settings.opentherm.dhwPresent && settings.dhw.enable, settings.opentherm.dhwPresent && settings.dhw.enable,
false, false,
@@ -133,20 +140,20 @@ protected:
settings.opentherm.dhwBlocking settings.opentherm.dhwBlocking
); );
if (!ot->isValidResponse(localResponse)) { if (!CustomOpenTherm::isValidResponse(response)) {
Log.swarningln(FPSTR(L_OT), F("Invalid response after setBoilerStatus: %s"), ot->statusToString(ot->getLastResponseStatus())); Log.swarningln(FPSTR(L_OT), F("Invalid response after setBoilerStatus: %s"), CustomOpenTherm::statusToString(this->instance->getLastResponseStatus()));
} }
if (!vars.states.otStatus && millis() - this->lastSuccessResponse < 1150) { if (!vars.states.otStatus && millis() - this->lastSuccessResponse < 1150) {
Log.sinfoln(FPSTR(L_OT), F("Connected. Initializing")); Log.sinfoln(FPSTR(L_OT), F("Connected"));
vars.states.otStatus = true; vars.states.otStatus = true;
this->initBoiler();
} else if (vars.states.otStatus && millis() - this->lastSuccessResponse > 1150) { } else if (vars.states.otStatus && millis() - this->lastSuccessResponse > 1150) {
Log.swarningln(FPSTR(L_OT), F("Disconnected")); Log.swarningln(FPSTR(L_OT), F("Disconnected"));
vars.states.otStatus = false; vars.states.otStatus = false;
this->isInitialized = false;
} }
// If boiler is disconnected, no need try setting other OT stuff // If boiler is disconnected, no need try setting other OT stuff
@@ -160,17 +167,27 @@ protected:
return; return;
} }
if (!this->isInitialized) {
Log.sinfoln(FPSTR(L_OT), F("Initializing..."));
this->isInitialized = true;
this->initializedTime = millis();
this->initializedMemberIdCode = settings.opentherm.memberIdCode;
this->dhwFlowRateMultiplier = 1;
this->pressureMultiplier = 1;
this->initialize();
}
if (vars.parameters.heatingEnabled != heatingEnabled) { if (vars.parameters.heatingEnabled != heatingEnabled) {
this->prevUpdateNonEssentialVars = 0; this->prevUpdateNonEssentialVars = 0;
vars.parameters.heatingEnabled = heatingEnabled; vars.parameters.heatingEnabled = heatingEnabled;
Log.sinfoln(FPSTR(L_OT_HEATING), "%s", heatingEnabled ? F("Enabled") : F("Disabled")); Log.sinfoln(FPSTR(L_OT_HEATING), "%s", heatingEnabled ? F("Enabled") : F("Disabled"));
} }
vars.states.heating = ot->isCentralHeatingActive(localResponse); vars.states.heating = CustomOpenTherm::isCentralHeatingActive(response);
vars.states.dhw = settings.opentherm.dhwPresent ? ot->isHotWaterActive(localResponse) : false; vars.states.dhw = settings.opentherm.dhwPresent ? CustomOpenTherm::isHotWaterActive(response) : false;
vars.states.flame = ot->isFlameOn(localResponse); vars.states.flame = CustomOpenTherm::isFlameOn(response);
vars.states.fault = ot->isFault(localResponse); vars.states.fault = CustomOpenTherm::isFault(response);
vars.states.diagnostic = ot->isDiagnostic(localResponse); vars.states.diagnostic = CustomOpenTherm::isDiagnostic(response);
// These parameters will be updated every minute // These parameters will be updated every minute
if (millis() - this->prevUpdateNonEssentialVars > 60000) { if (millis() - this->prevUpdateNonEssentialVars > 60000) {
@@ -193,7 +210,7 @@ protected:
// Get DHW min/max temp (if necessary) // Get DHW min/max temp (if necessary)
if (settings.opentherm.dhwPresent) { if (settings.opentherm.dhwPresent && settings.opentherm.getMinMaxTemp) {
if (updateMinMaxDhwTemp()) { if (updateMinMaxDhwTemp()) {
if (settings.dhw.minTemp < vars.parameters.dhwMinTemp) { if (settings.dhw.minTemp < vars.parameters.dhwMinTemp) {
settings.dhw.minTemp = vars.parameters.dhwMinTemp; settings.dhw.minTemp = vars.parameters.dhwMinTemp;
@@ -208,46 +225,51 @@ protected:
} }
} else { } else {
vars.parameters.dhwMinTemp = convertTemp(DEFAULT_DHW_MIN_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
vars.parameters.dhwMaxTemp = convertTemp(DEFAULT_DHW_MAX_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
Log.swarningln(FPSTR(L_OT_DHW), F("Failed get min/max temp")); Log.swarningln(FPSTR(L_OT_DHW), F("Failed get min/max temp"));
} }
if (settings.dhw.minTemp >= settings.dhw.maxTemp) { if (settings.dhw.minTemp >= settings.dhw.maxTemp) {
settings.dhw.minTemp = 30; settings.dhw.minTemp = vars.parameters.dhwMinTemp;
settings.dhw.maxTemp = 60; settings.dhw.maxTemp = vars.parameters.dhwMaxTemp;
fsSettings.update(); fsSettings.update();
} }
} }
// Get heating min/max temp // Get heating min/max temp
if (updateMinMaxHeatingTemp()) { if (settings.opentherm.getMinMaxTemp) {
if (settings.heating.minTemp < vars.parameters.heatingMinTemp) { if (updateMinMaxHeatingTemp()) {
settings.heating.minTemp = vars.parameters.heatingMinTemp; if (settings.heating.minTemp < vars.parameters.heatingMinTemp) {
fsSettings.update(); settings.heating.minTemp = vars.parameters.heatingMinTemp;
Log.snoticeln(FPSTR(L_OT_HEATING), F("Updated min temp: %hhu"), settings.heating.minTemp); fsSettings.update();
} Log.snoticeln(FPSTR(L_OT_HEATING), F("Updated min temp: %hhu"), settings.heating.minTemp);
}
if (settings.heating.maxTemp > vars.parameters.heatingMaxTemp) { if (settings.heating.maxTemp > vars.parameters.heatingMaxTemp) {
settings.heating.maxTemp = vars.parameters.heatingMaxTemp; settings.heating.maxTemp = vars.parameters.heatingMaxTemp;
fsSettings.update(); fsSettings.update();
Log.snoticeln(FPSTR(L_OT_HEATING), F("Updated max temp: %hhu"), settings.heating.maxTemp); Log.snoticeln(FPSTR(L_OT_HEATING), F("Updated max temp: %hhu"), settings.heating.maxTemp);
} }
} else { } else {
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed get min/max temp")); vars.parameters.heatingMinTemp = convertTemp(DEFAULT_HEATING_MIN_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
vars.parameters.heatingMaxTemp = convertTemp(DEFAULT_HEATING_MAX_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed get min/max temp"));
}
} }
if (settings.heating.minTemp >= settings.heating.maxTemp) { if (settings.heating.minTemp >= settings.heating.maxTemp) {
settings.heating.minTemp = 20; settings.heating.minTemp = vars.parameters.heatingMinTemp;
settings.heating.maxTemp = 90; settings.heating.maxTemp = vars.parameters.heatingMaxTemp;
fsSettings.update(); fsSettings.update();
} }
// Force set max heating temp
setMaxHeatingTemp(settings.heating.maxTemp);
// Get outdoor temp (if necessary) // Get outdoor temp (if necessary)
if (settings.sensors.outdoor.type == 0) { if (settings.sensors.outdoor.type == SensorType::BOILER) {
updateOutsideTemp(); updateOutsideTemp();
} }
@@ -263,7 +285,7 @@ protected:
// Get current modulation level (if necessary) // Get current modulation level (if necessary)
if ((settings.opentherm.dhwPresent && settings.dhw.enable) || settings.heating.enable || heatingEnabled) { if (vars.states.flame) {
updateModulationLevel(); updateModulationLevel();
} else { } else {
@@ -283,11 +305,17 @@ protected:
// Get current heating temp // Get current heating temp
updateHeatingTemp(); updateHeatingTemp();
// Get heating return temp
updateHeatingReturnTemp();
// Get exhaust temp
updateExhaustTemp();
// Fault reset action // Fault reset action
if (vars.actions.resetFault) { if (vars.actions.resetFault) {
if (vars.states.fault) { if (vars.states.fault) {
if (ot->sendBoilerReset()) { if (this->instance->sendBoilerReset()) {
Log.sinfoln(FPSTR(L_OT), F("Boiler fault reset successfully")); Log.sinfoln(FPSTR(L_OT), F("Boiler fault reset successfully"));
} else { } else {
@@ -301,7 +329,7 @@ protected:
// Diag reset action // Diag reset action
if (vars.actions.resetDiagnostic) { if (vars.actions.resetDiagnostic) {
if (vars.states.diagnostic) { if (vars.states.diagnostic) {
if (ot->sendServiceReset()) { if (this->instance->sendServiceReset()) {
Log.sinfoln(FPSTR(L_OT), F("Boiler diagnostic reset successfully")); Log.sinfoln(FPSTR(L_OT), F("Boiler diagnostic reset successfully"));
} else { } else {
@@ -315,15 +343,16 @@ protected:
// Update DHW temp // Update DHW temp
byte newDhwTemp = settings.dhw.target; byte newDhwTemp = settings.dhw.target;
if (settings.opentherm.dhwPresent && settings.dhw.enable && (needSetDhwTemp() || newDhwTemp != currentDhwTemp)) { if (settings.opentherm.dhwPresent && settings.dhw.enable && (this->needSetDhwTemp() || newDhwTemp != currentDhwTemp)) {
if (newDhwTemp < settings.dhw.minTemp || newDhwTemp > settings.dhw.maxTemp) { if (newDhwTemp < settings.dhw.minTemp || newDhwTemp > settings.dhw.maxTemp) {
newDhwTemp = constrain(newDhwTemp, settings.dhw.minTemp, settings.dhw.maxTemp); newDhwTemp = constrain(newDhwTemp, settings.dhw.minTemp, settings.dhw.maxTemp);
} }
Log.sinfoln(FPSTR(L_OT_DHW), F("Set temp = %u"), newDhwTemp); float convertedTemp = convertTemp(newDhwTemp, settings.system.unitSystem, settings.opentherm.unitSystem);
Log.sinfoln(FPSTR(L_OT_DHW), F("Set temp: %u (converted: %.2f)"), newDhwTemp, convertedTemp);
// Set DHW temp // Set DHW temp
if (ot->setDhwTemp(newDhwTemp)) { if (this->instance->setDhwTemp(convertedTemp)) {
currentDhwTemp = newDhwTemp; currentDhwTemp = newDhwTemp;
this->dhwSetTempTime = millis(); this->dhwSetTempTime = millis();
@@ -333,7 +362,7 @@ protected:
// Set DHW temp to CH2 // Set DHW temp to CH2
if (settings.opentherm.dhwToCh2) { if (settings.opentherm.dhwToCh2) {
if (!ot->setHeatingCh2Temp(newDhwTemp)) { if (!this->instance->setHeatingCh2Temp(convertedTemp)) {
Log.swarningln(FPSTR(L_OT_DHW), F("Failed set ch2 temp")); Log.swarningln(FPSTR(L_OT_DHW), F("Failed set ch2 temp"));
} }
} }
@@ -341,11 +370,12 @@ protected:
// Update heating temp // Update heating temp
if (heatingEnabled && (needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001)) { if (heatingEnabled && (this->needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001)) {
Log.sinfoln(FPSTR(L_OT_HEATING), F("Set temp = %u"), vars.parameters.heatingSetpoint); float convertedTemp = convertTemp(vars.parameters.heatingSetpoint, settings.system.unitSystem, settings.opentherm.unitSystem);
Log.sinfoln(FPSTR(L_OT_HEATING), F("Set temp: %u (converted: %.2f)"), vars.parameters.heatingSetpoint, convertedTemp);
// Set heating temp // Set heating temp
if (ot->setHeatingCh1Temp(vars.parameters.heatingSetpoint)) { if (this->instance->setHeatingCh1Temp(convertedTemp) || this->setMaxHeatingTemp(convertedTemp)) {
currentHeatingTemp = vars.parameters.heatingSetpoint; currentHeatingTemp = vars.parameters.heatingSetpoint;
this->heatingSetTempTime = millis(); this->heatingSetTempTime = millis();
@@ -355,7 +385,7 @@ protected:
// Set heating temp to CH2 // Set heating temp to CH2
if (settings.opentherm.heatingCh1ToCh2) { if (settings.opentherm.heatingCh1ToCh2) {
if (!ot->setHeatingCh2Temp(vars.parameters.heatingSetpoint)) { if (!this->instance->setHeatingCh2Temp(convertedTemp)) {
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set ch2 temp")); Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set ch2 temp"));
} }
} }
@@ -378,8 +408,40 @@ protected:
} }
} }
void initialize() {
// Not all boilers support these, only try once when the boiler becomes connected
if (this->updateSlaveVersion()) {
Log.straceln(FPSTR(L_OT), F("Slave version: %u, type: %u"), vars.parameters.slaveVersion, vars.parameters.slaveType);
} else {
Log.swarningln(FPSTR(L_OT), F("Get slave version failed"));
}
// 0x013F
if (this->setMasterVersion(0x3F, 0x01)) {
Log.straceln(FPSTR(L_OT), F("Master version: %u, type: %u"), vars.parameters.masterVersion, vars.parameters.masterType);
} else {
Log.swarningln(FPSTR(L_OT), F("Set master version failed"));
}
if (this->updateSlaveConfig()) {
Log.straceln(FPSTR(L_OT), F("Slave member id: %u, flags: %u"), vars.parameters.slaveMemberId, vars.parameters.slaveFlags);
} else {
Log.swarningln(FPSTR(L_OT), F("Get slave config failed"));
}
if (this->setMasterConfig(settings.opentherm.memberIdCode & 0xFF, (settings.opentherm.memberIdCode & 0xFFFF) >> 8)) {
Log.straceln(FPSTR(L_OT), F("Master member id: %u, flags: %u"), vars.parameters.masterMemberId, vars.parameters.masterFlags);
} else {
Log.swarningln(FPSTR(L_OT), F("Set master config failed"));
}
}
bool isReady() { bool isReady() {
return millis() - this->startupTime > this->readyTime; return millis() - this->instanceCreatedTime > this->readyTime;
} }
bool needSetDhwTemp() { bool needSetDhwTemp() {
@@ -391,13 +453,13 @@ protected:
} }
bool updateSlaveConfig() { bool updateSlaveConfig() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA, OpenThermRequestType::READ_DATA,
OpenThermMessageID::SConfigSMemberIDcode, OpenThermMessageID::SConfigSMemberIDcode,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
@@ -452,69 +514,69 @@ protected:
return true; return true;
} }
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::WRITE_DATA, OpenThermRequestType::WRITE_DATA,
OpenThermMessageID::MConfigMMemberIDcode, OpenThermMessageID::MConfigMMemberIDcode,
request request
)); ));
return ot->isValidResponse(response); return CustomOpenTherm::isValidResponse(response);
} }
bool setMaxModulationLevel(byte value) { bool setMaxModulationLevel(byte value) {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::WRITE_DATA, OpenThermRequestType::WRITE_DATA,
OpenThermMessageID::MaxRelModLevelSetting, OpenThermMessageID::MaxRelModLevelSetting,
ot->toF88(value) CustomOpenTherm::toFloat(value)
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
vars.parameters.maxModulation = ot->fromF88(response); vars.parameters.maxModulation = CustomOpenTherm::getFloat(response);
return true; return true;
} }
bool updateSlaveOtVersion() { bool updateSlaveOtVersion() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA, OpenThermRequestType::READ_DATA,
OpenThermMessageID::OpenThermVersionSlave, OpenThermMessageID::OpenThermVersionSlave,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
vars.parameters.slaveOtVersion = ot->getFloat(response); vars.parameters.slaveOtVersion = CustomOpenTherm::getFloat(response);
return true; return true;
} }
bool setMasterOtVersion(float version) { bool setMasterOtVersion(float version) {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::WRITE_DATA, OpenThermRequestType::WRITE_DATA,
OpenThermMessageID::OpenThermVersionMaster, OpenThermMessageID::OpenThermVersionMaster,
ot->toF88(version) CustomOpenTherm::toFloat(version)
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
vars.parameters.masterOtVersion = ot->fromF88(response); vars.parameters.masterOtVersion = CustomOpenTherm::getFloat(response);
return true; return true;
} }
bool updateSlaveVersion() { bool updateSlaveVersion() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA, OpenThermRequestType::READ_DATA,
OpenThermMessageID::SlaveVersion, OpenThermMessageID::SlaveVersion,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
@@ -525,13 +587,13 @@ protected:
} }
bool setMasterVersion(uint8_t version, uint8_t type) { bool setMasterVersion(uint8_t version, uint8_t type) {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::WRITE_DATA, OpenThermRequestType::WRITE_DATA,
OpenThermMessageID::MasterVersion, OpenThermMessageID::MasterVersion,
(unsigned int) version | (unsigned int) type << 8 (unsigned int) version | (unsigned int) type << 8
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
@@ -542,13 +604,13 @@ protected:
} }
bool updateMinMaxDhwTemp() { bool updateMinMaxDhwTemp() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA, OpenThermRequestType::READ_DATA,
OpenThermMessageID::TdhwSetUBTdhwSetLB, OpenThermMessageID::TdhwSetUBTdhwSetLB,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
@@ -556,8 +618,8 @@ protected:
byte maxTemp = (response & 0xFFFF) >> 8; byte maxTemp = (response & 0xFFFF) >> 8;
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) { if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
vars.parameters.dhwMinTemp = minTemp; vars.parameters.dhwMinTemp = convertTemp(minTemp, settings.opentherm.unitSystem, settings.system.unitSystem);
vars.parameters.dhwMaxTemp = maxTemp; vars.parameters.dhwMaxTemp = convertTemp(maxTemp, settings.opentherm.unitSystem, settings.system.unitSystem);
return true; return true;
} }
@@ -566,13 +628,13 @@ protected:
} }
bool updateMinMaxHeatingTemp() { bool updateMinMaxHeatingTemp() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA, OpenThermRequestType::READ_DATA,
OpenThermMessageID::MaxTSetUBMaxTSetLB, OpenThermMessageID::MaxTSetUBMaxTSetLB,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
@@ -580,8 +642,8 @@ protected:
byte maxTemp = (response & 0xFFFF) >> 8; byte maxTemp = (response & 0xFFFF) >> 8;
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) { if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
vars.parameters.heatingMinTemp = minTemp; vars.parameters.heatingMinTemp = convertTemp(minTemp, settings.opentherm.unitSystem, settings.system.unitSystem);
vars.parameters.heatingMaxTemp = maxTemp; vars.parameters.heatingMaxTemp = convertTemp(maxTemp, settings.opentherm.unitSystem, settings.system.unitSystem);
return true; return true;
} }
@@ -589,99 +651,164 @@ protected:
} }
bool setMaxHeatingTemp(byte value) { bool setMaxHeatingTemp(byte value) {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermMessageType::WRITE_DATA, OpenThermMessageType::WRITE_DATA,
OpenThermMessageID::MaxTSet, OpenThermMessageID::MaxTSet,
ot->temperatureToData(value) CustomOpenTherm::temperatureToData(value)
)); ));
return ot->isValidResponse(response); return CustomOpenTherm::isValidResponse(response);
} }
bool updateOutsideTemp() { bool updateOutsideTemp() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA, OpenThermRequestType::READ_DATA,
OpenThermMessageID::Toutside, OpenThermMessageID::Toutside,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
vars.temperatures.outdoor = ot->getFloat(response) + settings.sensors.outdoor.offset; vars.temperatures.outdoor = settings.sensors.outdoor.offset + convertTemp(
CustomOpenTherm::getFloat(response),
settings.opentherm.unitSystem,
settings.system.unitSystem
);
return true;
}
bool updateExhaustTemp() {
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA,
OpenThermMessageID::Texhaust,
0
));
if (!CustomOpenTherm::isValidResponse(response)) {
return false;
}
float value = (float) CustomOpenTherm::getInt(response);
if (!isValidTemp(value, settings.opentherm.unitSystem, -40, 500)) {
return false;
}
vars.temperatures.exhaust = convertTemp(
value,
settings.opentherm.unitSystem,
settings.system.unitSystem
);
return true; return true;
} }
bool updateHeatingTemp() { bool updateHeatingTemp() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermMessageType::READ_DATA, OpenThermMessageType::READ_DATA,
OpenThermMessageID::Tboiler, OpenThermMessageID::Tboiler,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
float value = ot->getFloat(response); float value = CustomOpenTherm::getFloat(response);
if (value <= 0) { if (value <= 0) {
return false; return false;
} }
vars.temperatures.heating = value; vars.temperatures.heating = convertTemp(
value,
settings.opentherm.unitSystem,
settings.system.unitSystem
);
return true;
}
bool updateHeatingReturnTemp() {
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermMessageType::READ_DATA,
OpenThermMessageID::Tret,
0
));
if (!CustomOpenTherm::isValidResponse(response)) {
return false;
}
vars.temperatures.heatingReturn = convertTemp(
CustomOpenTherm::getFloat(response),
settings.opentherm.unitSystem,
settings.system.unitSystem
);
return true; return true;
} }
bool updateDhwTemp() { bool updateDhwTemp() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermMessageType::READ_DATA, OpenThermMessageType::READ_DATA,
OpenThermMessageID::Tdhw, OpenThermMessageID::Tdhw,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
float value = ot->getFloat(response); float value = CustomOpenTherm::getFloat(response);
if (value <= 0) { if (value <= 0) {
return false; return false;
} }
vars.temperatures.dhw = value; vars.temperatures.dhw = convertTemp(
value,
settings.opentherm.unitSystem,
settings.system.unitSystem
);
return true; return true;
} }
bool updateDhwFlowRate() { bool updateDhwFlowRate() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermMessageType::READ_DATA, OpenThermMessageType::READ_DATA,
OpenThermMessageID::DHWFlowRate, OpenThermMessageID::DHWFlowRate,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
float value = ot->getFloat(response); float value = CustomOpenTherm::getFloat(response);
if (value > 16 && this->dhwFlowRateMultiplier != 10) { if (this->dhwFlowRateMultiplier != 10 && value > convertVolume(16, UnitSystem::METRIC, settings.opentherm.unitSystem)) {
this->dhwFlowRateMultiplier = 10; this->dhwFlowRateMultiplier = 10;
} }
vars.sensors.dhwFlowRate = this->dhwFlowRateMultiplier == 1 ? value : value / this->dhwFlowRateMultiplier;
vars.sensors.dhwFlowRate = convertVolume(
value / this->dhwFlowRateMultiplier,
settings.opentherm.unitSystem,
settings.system.unitSystem
);
return true; return true;
} }
bool updateFaultCode() { bool updateFaultCode() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA, OpenThermRequestType::READ_DATA,
OpenThermMessageID::ASFflags, OpenThermMessageID::ASFflags,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
@@ -690,42 +817,42 @@ protected:
} }
bool updateModulationLevel() { bool updateModulationLevel() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA, OpenThermRequestType::READ_DATA,
OpenThermMessageID::RelModLevel, OpenThermMessageID::RelModLevel,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
float modulation = ot->fromF88(response); vars.sensors.modulation = CustomOpenTherm::getFloat(response);
if (!vars.states.flame) {
vars.sensors.modulation = 0;
} else {
vars.sensors.modulation = modulation;
}
return true; return true;
} }
bool updatePressure() { bool updatePressure() {
unsigned long response = ot->sendRequest(ot->buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA, OpenThermRequestType::READ_DATA,
OpenThermMessageID::CHPressure, OpenThermMessageID::CHPressure,
0 0
)); ));
if (!ot->isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
float value = ot->getFloat(response); float value = CustomOpenTherm::getFloat(response);
if (value > 5 && this->pressureMultiplier != 10) { if (this->pressureMultiplier != 10 && value > convertPressure(5, UnitSystem::METRIC, settings.opentherm.unitSystem)) {
this->pressureMultiplier = 10; this->pressureMultiplier = 10;
} }
vars.sensors.pressure = this->pressureMultiplier == 1 ? value : value / this->pressureMultiplier;
vars.sensors.pressure = convertPressure(
value / this->pressureMultiplier,
settings.opentherm.unitSystem,
settings.system.unitSystem
);
return true; return true;
} }

View File

@@ -62,7 +62,7 @@ protected:
}*/ }*/
int getTaskPriority() { int getTaskPriority() {
return 0; return 1;
} }
void setup() { void setup() {
@@ -434,6 +434,7 @@ protected:
JsonDocument doc; JsonDocument doc;
varsToJson(vars, doc); varsToJson(vars, doc);
doc["system"]["unitSystem"] = static_cast<byte>(settings.system.unitSystem);
doc["system"]["version"] = PROJECT_VERSION; doc["system"]["version"] = PROJECT_VERSION;
doc["system"]["buildDate"] = __DATE__ " " __TIME__; doc["system"]["buildDate"] = __DATE__ " " __TIME__;
doc["system"]["uptime"] = millis() / 1000ul; doc["system"]["uptime"] = millis() / 1000ul;

View File

@@ -69,7 +69,7 @@ protected:
} }
} }
// Ограничиваем, если до этого не ограничило // Limits
if (newTemp < settings.heating.minTemp || newTemp > settings.heating.maxTemp) { if (newTemp < settings.heating.minTemp || newTemp > settings.heating.maxTemp) {
newTemp = constrain(newTemp, settings.heating.minTemp, settings.heating.maxTemp); newTemp = constrain(newTemp, settings.heating.minTemp, settings.heating.maxTemp);
} }
@@ -84,7 +84,7 @@ protected:
float newTemp = 0; float newTemp = 0;
// if use equitherm // if use equitherm
if (settings.emergency.useEquitherm && settings.sensors.outdoor.type != 1) { if (settings.emergency.useEquitherm && settings.sensors.outdoor.type != SensorType::MANUAL) {
float etResult = getEquithermTemp(settings.heating.minTemp, settings.heating.maxTemp); float etResult = getEquithermTemp(settings.heating.minTemp, settings.heating.maxTemp);
if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) { if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) {
@@ -97,7 +97,7 @@ protected:
newTemp += prevEtResult; newTemp += prevEtResult;
} }
} else if(settings.emergency.usePid && settings.sensors.indoor.type != 1) { } else if(settings.emergency.usePid && settings.sensors.indoor.type != SensorType::MANUAL) {
if (vars.parameters.heatingEnabled) { if (vars.parameters.heatingEnabled) {
float pidResult = getPidTemp( float pidResult = getPidTemp(
settings.heating.minTemp, settings.heating.minTemp,
@@ -182,7 +182,6 @@ protected:
} }
newTemp = round(newTemp); newTemp = round(newTemp);
newTemp = constrain(newTemp, 0, 100);
return newTemp; return newTemp;
} }
@@ -273,16 +272,36 @@ protected:
} }
} }
/**
* @brief Get the Equitherm Temp
* Calculations in degrees C, conversion occurs when using F
*
* @param minTemp
* @param maxTemp
* @return float
*/
float getEquithermTemp(int minTemp, int maxTemp) { float getEquithermTemp(int minTemp, int maxTemp) {
float targetTemp = vars.states.emergency ? settings.emergency.target : settings.heating.target;
float indoorTemp = vars.temperatures.indoor;
float outdoorTemp = vars.temperatures.outdoor;
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
minTemp = f2c(minTemp);
maxTemp = f2c(maxTemp);
targetTemp = f2c(targetTemp);
indoorTemp = f2c(indoorTemp);
outdoorTemp = f2c(outdoorTemp);
}
if (vars.states.emergency) { if (vars.states.emergency) {
etRegulator.Kt = 0; etRegulator.Kt = 0;
etRegulator.indoorTemp = 0; etRegulator.indoorTemp = 0;
etRegulator.outdoorTemp = vars.temperatures.outdoor; etRegulator.outdoorTemp = outdoorTemp;
} else if (settings.pid.enable) { } else if (settings.pid.enable) {
etRegulator.Kt = 0; etRegulator.Kt = 0;
etRegulator.indoorTemp = round(vars.temperatures.indoor); etRegulator.indoorTemp = round(indoorTemp);
etRegulator.outdoorTemp = round(vars.temperatures.outdoor); etRegulator.outdoorTemp = round(outdoorTemp);
} else { } else {
if (settings.heating.turbo) { if (settings.heating.turbo) {
@@ -290,17 +309,21 @@ protected:
} else { } else {
etRegulator.Kt = settings.equitherm.t_factor; etRegulator.Kt = settings.equitherm.t_factor;
} }
etRegulator.indoorTemp = vars.temperatures.indoor; etRegulator.indoorTemp = indoorTemp;
etRegulator.outdoorTemp = vars.temperatures.outdoor; etRegulator.outdoorTemp = outdoorTemp;
} }
etRegulator.setLimits(minTemp, maxTemp); etRegulator.setLimits(minTemp, maxTemp);
etRegulator.Kn = settings.equitherm.n_factor; etRegulator.Kn = settings.equitherm.n_factor;
// etRegulator.Kn = tuneEquithermN(etRegulator.Kn, vars.temperatures.indoor, settings.heating.target, 300, 1800, 0.01, 1);
etRegulator.Kk = settings.equitherm.k_factor; etRegulator.Kk = settings.equitherm.k_factor;
etRegulator.targetTemp = vars.states.emergency ? settings.emergency.target : settings.heating.target; etRegulator.targetTemp = targetTemp;
float result = etRegulator.getResult();
return etRegulator.getResult(); if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
result = c2f(result);
}
return result;
} }
float getPidTemp(int minTemp, int maxTemp) { float getPidTemp(int minTemp, int maxTemp) {

View File

@@ -60,39 +60,58 @@ protected:
} }
void loop() { void loop() {
bool needUpdateIndoorTemp = false; bool indoorTempUpdated = false;
bool needUpdateOutdoorTemp = false; bool outdoorTempUpdated = false;
if (settings.sensors.outdoor.type == 2 && settings.sensors.outdoor.pin) { if (settings.sensors.outdoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.indoor.gpio)) {
outdoorTemperatureSensor(); outdoorTemperatureSensor();
needUpdateOutdoorTemp = true; outdoorTempUpdated = true;
} }
if (settings.sensors.indoor.type == 2 && settings.sensors.indoor.pin) { if (settings.sensors.indoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.indoor.gpio)) {
indoorTemperatureSensor(); indoorTemperatureSensor();
needUpdateIndoorTemp = true; indoorTempUpdated = true;
}
#if USE_BLE
else if (settings.sensors.indoor.type == SensorType::BLUETOOTH) {
indoorTemperatureBluetoothSensor();
indoorTempUpdated = true;
}
#endif
if (outdoorTempUpdated) {
float newTemp = settings.sensors.outdoor.offset;
if (settings.system.unitSystem == UnitSystem::METRIC) {
newTemp += this->filteredOutdoorTemp;
} else if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
newTemp += c2f(this->filteredOutdoorTemp);
}
if (fabs(vars.temperatures.outdoor - newTemp) > 0.099) {
vars.temperatures.outdoor = newTemp;
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("New temp: %f"), vars.temperatures.outdoor);
}
} }
#if USE_BLE if (indoorTempUpdated) {
if (settings.sensors.indoor.type == 3) { float newTemp = settings.sensors.indoor.offset;
bluetoothSensor(); if (settings.system.unitSystem == UnitSystem::METRIC) {
needUpdateIndoorTemp = true; newTemp += this->filteredIndoorTemp;
}
#endif
if (needUpdateOutdoorTemp && fabs(vars.temperatures.outdoor - this->filteredOutdoorTemp) > 0.099) { } else if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
vars.temperatures.outdoor = this->filteredOutdoorTemp + settings.sensors.outdoor.offset; newTemp += c2f(this->filteredIndoorTemp);
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("New temp: %f"), vars.temperatures.outdoor); }
}
if (needUpdateIndoorTemp && fabs(vars.temperatures.indoor - this->filteredIndoorTemp) > 0.099) { if (fabs(vars.temperatures.indoor - newTemp) > 0.099) {
vars.temperatures.indoor = this->filteredIndoorTemp + settings.sensors.indoor.offset; vars.temperatures.indoor = newTemp;
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("New temp: %f"), vars.temperatures.indoor); Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("New temp: %f"), vars.temperatures.indoor);
}
} }
} }
#if USE_BLE #if USE_BLE
void bluetoothSensor() { void indoorTemperatureBluetoothSensor() {
static bool initBleNotify = false; static bool initBleNotify = false;
if (!initBleSensor && millis() > 5000) { if (!initBleSensor && millis() > 5000) {
Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Init BLE")); Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Init BLE"));
@@ -210,9 +229,9 @@ protected:
void outdoorTemperatureSensor() { void outdoorTemperatureSensor() {
if (!this->initOutdoorSensor) { if (!this->initOutdoorSensor) {
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("Starting on gpio %hhu..."), settings.sensors.outdoor.pin); Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("Starting on gpio %hhu..."), settings.sensors.outdoor.gpio);
this->oneWireOutdoorSensor->begin(settings.sensors.outdoor.pin); this->oneWireOutdoorSensor->begin(settings.sensors.outdoor.gpio);
this->outdoorSensor->begin(); this->outdoorSensor->begin();
Log.straceln( Log.straceln(
@@ -276,9 +295,9 @@ protected:
void indoorTemperatureSensor() { void indoorTemperatureSensor() {
if (!this->initIndoorSensor) { if (!this->initIndoorSensor) {
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("Starting on gpio %hhu..."), settings.sensors.indoor.pin); Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("Starting on gpio %hhu..."), settings.sensors.indoor.gpio);
this->oneWireIndoorSensor->begin(settings.sensors.indoor.pin); this->oneWireIndoorSensor->begin(settings.sensors.indoor.gpio);
this->indoorSensor->begin(); this->indoorSensor->begin();
Log.straceln( Log.straceln(

View File

@@ -1,5 +1,5 @@
struct NetworkSettings { struct NetworkSettings {
char hostname[25] = HOSTNAME_DEFAULT; char hostname[25] = DEFAULT_HOSTNAME;
bool useDhcp = true; bool useDhcp = true;
struct { struct {
@@ -10,14 +10,14 @@ struct NetworkSettings {
} staticConfig; } staticConfig;
struct { struct {
char ssid[33] = AP_SSID_DEFAULT; char ssid[33] = DEFAULT_AP_SSID;
char password[65] = AP_PASSWORD_DEFAULT; char password[65] = DEFAULT_AP_PASSWORD;
byte channel = 6; byte channel = 6;
} ap; } ap;
struct { struct {
char ssid[33] = STA_SSID_DEFAULT; char ssid[33] = DEFAULT_STA_SSID;
char password[65] = STA_PASSWORD_DEFAULT; char password[65] = DEFAULT_STA_PASSWORD;
byte channel = 0; byte channel = 0;
} sta; } sta;
} networkSettings; } networkSettings;
@@ -27,17 +27,19 @@ struct Settings {
bool debug = DEBUG_BY_DEFAULT; bool debug = DEBUG_BY_DEFAULT;
bool useSerial = USE_SERIAL; bool useSerial = USE_SERIAL;
bool useTelnet = USE_TELNET; bool useTelnet = USE_TELNET;
UnitSystem unitSystem = UnitSystem::METRIC;
} system; } system;
struct { struct {
bool useAuth = false; bool useAuth = false;
char login[13] = PORTAL_LOGIN_DEFAULT; char login[13] = DEFAULT_PORTAL_LOGIN;
char password[33] = PORTAL_PASSWORD_DEFAULT; char password[33] = DEFAULT_PORTAL_PASSWORD;
} portal; } portal;
struct { struct {
byte inPin = OT_IN_PIN_DEFAULT; UnitSystem unitSystem = UnitSystem::METRIC;
byte outPin = OT_OUT_PIN_DEFAULT; byte inGpio = DEFAULT_OT_IN_GPIO;
byte outGpio = DEFAULT_OT_OUT_GPIO;
unsigned int memberIdCode = 0; unsigned int memberIdCode = 0;
bool dhwPresent = true; bool dhwPresent = true;
bool summerWinterMode = false; bool summerWinterMode = false;
@@ -46,14 +48,15 @@ struct Settings {
bool dhwToCh2 = false; bool dhwToCh2 = false;
bool dhwBlocking = false; bool dhwBlocking = false;
bool modulationSyncWithHeating = false; bool modulationSyncWithHeating = false;
bool getMinMaxTemp = true;
} opentherm; } opentherm;
struct { struct {
char server[81] = MQTT_SERVER_DEFAULT; char server[81] = DEFAULT_MQTT_SERVER;
unsigned short port = MQTT_PORT_DEFAULT; unsigned short port = DEFAULT_MQTT_PORT;
char user[33] = MQTT_USER_DEFAULT; char user[33] = DEFAULT_MQTT_USER;
char password[33] = MQTT_PASSWORD_DEFAULT; char password[33] = DEFAULT_MQTT_PASSWORD;
char prefix[33] = MQTT_PREFIX_DEFAULT; char prefix[33] = DEFAULT_MQTT_PREFIX;
unsigned short interval = 5; unsigned short interval = 5;
} mqtt; } mqtt;
@@ -100,16 +103,14 @@ struct Settings {
struct { struct {
struct { struct {
// 0 - boiler, 1 - manual, 2 - ds18b20 SensorType type = SensorType::BOILER;
byte type = 0; byte gpio = DEFAULT_SENSOR_OUTDOOR_GPIO;
byte pin = SENSOR_OUTDOOR_PIN_DEFAULT;
float offset = 0.0f; float offset = 0.0f;
} outdoor; } outdoor;
struct { struct {
// 1 - manual, 2 - ds18b20, 3 - ble SensorType type = SensorType::MANUAL;
byte type = 1; byte gpio = DEFAULT_SENSOR_INDOOR_GPIO;
byte pin = SENSOR_INDOOR_PIN_DEFAULT;
uint8_t bleAddresss[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t bleAddresss[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
float offset = 0.0f; float offset = 0.0f;
} indoor; } indoor;
@@ -117,7 +118,7 @@ struct Settings {
struct { struct {
bool use = false; bool use = false;
byte pin = EXT_PUMP_PIN_DEFAULT; byte gpio = DEFAULT_EXT_PUMP_GPIO;
unsigned short postCirculationTime = 600; unsigned short postCirculationTime = 600;
unsigned int antiStuckInterval = 2592000; unsigned int antiStuckInterval = 2592000;
unsigned short antiStuckTime = 300; unsigned short antiStuckTime = 300;
@@ -155,7 +156,9 @@ struct Variables {
float indoor = 0.0f; float indoor = 0.0f;
float outdoor = 0.0f; float outdoor = 0.0f;
float heating = 0.0f; float heating = 0.0f;
float heatingReturn = 0.0f;
float dhw = 0.0f; float dhw = 0.0f;
float exhaust = 0.0f;
} temperatures; } temperatures;
struct { struct {

View File

@@ -1,12 +1,9 @@
#define PROJECT_NAME "OpenTherm Gateway" #define PROJECT_NAME "OpenTherm Gateway"
#define PROJECT_VERSION "1.4.0-rc.16" #define PROJECT_VERSION "1.4.0-rc.20"
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway" #define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
#define EMERGENCY_TIME_TRESHOLD 120000 #define EMERGENCY_TIME_TRESHOLD 120000
#define MQTT_RECONNECT_INTERVAL 15000 #define MQTT_RECONNECT_INTERVAL 15000
#define MQTT_KEEPALIVE 30
#define OPENTHERM_OFFLINE_TRESHOLD 10
#define EXT_SENSORS_INTERVAL 5000 #define EXT_SENSORS_INTERVAL 5000
#define EXT_SENSORS_FILTER_K 0.15 #define EXT_SENSORS_FILTER_K 0.15
@@ -14,16 +11,12 @@
#define CONFIG_URL "http://%s/" #define CONFIG_URL "http://%s/"
#define SETTINGS_VALID_VALUE "stvalid" // only 8 chars! #define SETTINGS_VALID_VALUE "stvalid" // only 8 chars!
#define GPIO_IS_NOT_CONFIGURED 0xff
#define DEFAULT_HEATING_MIN_TEMP 20 #define DEFAULT_HEATING_MIN_TEMP 20
#define DEFAULT_HEATING_MAX_TEMP 90 #define DEFAULT_HEATING_MAX_TEMP 90
#define DEFAULT_DHW_MIN_TEMP 30 #define DEFAULT_DHW_MIN_TEMP 30
#define DEFAULT_DHW_MAX_TEMP 60 #define DEFAULT_DHW_MAX_TEMP 60
#ifndef WM_DEBUG_MODE
#define WM_DEBUG_MODE WM_DEBUG_NOTIFY
#endif
#ifndef USE_SERIAL #ifndef USE_SERIAL
#define USE_SERIAL true #define USE_SERIAL true
#endif #endif
@@ -36,80 +29,98 @@
#define USE_BLE false #define USE_BLE false
#endif #endif
#ifndef HOSTNAME_DEFAULT #ifndef DEFAULT_HOSTNAME
#define HOSTNAME_DEFAULT "opentherm" #define DEFAULT_HOSTNAME "opentherm"
#endif #endif
#ifndef AP_SSID_DEFAULT #ifndef DEFAULT_AP_SSID
#define AP_SSID_DEFAULT "OpenTherm Gateway" #define DEFAULT_AP_SSID "OpenTherm Gateway"
#endif #endif
#ifndef AP_PASSWORD_DEFAULT #ifndef DEFAULT_AP_PASSWORD
#define AP_PASSWORD_DEFAULT "otgateway123456" #define DEFAULT_AP_PASSWORD "otgateway123456"
#endif #endif
#ifndef STA_SSID_DEFAULT #ifndef DEFAULT_STA_SSID
#define STA_SSID_DEFAULT "" #define DEFAULT_STA_SSID ""
#endif #endif
#ifndef STA_PASSWORD_DEFAULT #ifndef DEFAULT_STA_PASSWORD
#define STA_PASSWORD_DEFAULT "" #define DEFAULT_STA_PASSWORD ""
#endif #endif
#ifndef DEBUG_BY_DEFAULT #ifndef DEBUG_BY_DEFAULT
#define DEBUG_BY_DEFAULT false #define DEBUG_BY_DEFAULT false
#endif #endif
#ifndef PORTAL_LOGIN_DEFAULT #ifndef DEFAULT_PORTAL_LOGIN
#define PORTAL_LOGIN_DEFAULT "" #define DEFAULT_PORTAL_LOGIN ""
#endif #endif
#ifndef PORTAL_PASSWORD_DEFAULT #ifndef DEFAULT_PORTAL_PASSWORD
#define PORTAL_PASSWORD_DEFAULT "" #define DEFAULT_PORTAL_PASSWORD ""
#endif #endif
#ifndef MQTT_SERVER_DEFAULT #ifndef DEFAULT_MQTT_SERVER
#define MQTT_SERVER_DEFAULT "" #define DEFAULT_MQTT_SERVER ""
#endif #endif
#ifndef MQTT_PORT_DEFAULT #ifndef DEFAULT_MQTT_PORT
#define MQTT_PORT_DEFAULT 1883 #define DEFAULT_MQTT_PORT 1883
#endif #endif
#ifndef MQTT_USER_DEFAULT #ifndef DEFAULT_MQTT_USER
#define MQTT_USER_DEFAULT "" #define DEFAULT_MQTT_USER ""
#endif #endif
#ifndef MQTT_PASSWORD_DEFAULT #ifndef DEFAULT_MQTT_PASSWORD
#define MQTT_PASSWORD_DEFAULT "" #define DEFAULT_MQTT_PASSWORD ""
#endif #endif
#ifndef MQTT_PREFIX_DEFAULT #ifndef DEFAULT_MQTT_PREFIX
#define MQTT_PREFIX_DEFAULT "opentherm" #define DEFAULT_MQTT_PREFIX "opentherm"
#endif #endif
#ifndef OT_IN_PIN_DEFAULT #ifndef DEFAULT_OT_IN_GPIO
#define OT_IN_PIN_DEFAULT 0 #define DEFAULT_OT_IN_GPIO GPIO_IS_NOT_CONFIGURED
#endif #endif
#ifndef OT_OUT_PIN_DEFAULT #ifndef DEFAULT_OT_OUT_GPIO
#define OT_OUT_PIN_DEFAULT 0 #define DEFAULT_OT_OUT_GPIO GPIO_IS_NOT_CONFIGURED
#endif #endif
#ifndef SENSOR_OUTDOOR_PIN_DEFAULT #ifndef DEFAULT_SENSOR_OUTDOOR_GPIO
#define SENSOR_OUTDOOR_PIN_DEFAULT 0 #define DEFAULT_SENSOR_OUTDOOR_GPIO GPIO_IS_NOT_CONFIGURED
#endif #endif
#ifndef SENSOR_INDOOR_PIN_DEFAULT #ifndef DEFAULT_SENSOR_INDOOR_GPIO
#define SENSOR_INDOOR_PIN_DEFAULT 0 #define DEFAULT_SENSOR_INDOOR_GPIO GPIO_IS_NOT_CONFIGURED
#endif #endif
#ifndef EXT_PUMP_PIN_DEFAULT #ifndef DEFAULT_EXT_PUMP_GPIO
#define EXT_PUMP_PIN_DEFAULT 0 #define DEFAULT_EXT_PUMP_GPIO GPIO_IS_NOT_CONFIGURED
#endif #endif
#ifndef PROGMEM #ifndef PROGMEM
#define PROGMEM #define PROGMEM
#endif #endif
#ifndef GPIO_IS_VALID_GPIO
#define GPIO_IS_VALID_GPIO(gpioNum) (gpioNum >= 0 && gpioNum <= 16)
#endif
#define GPIO_IS_VALID(gpioNum) (gpioNum != GPIO_IS_NOT_CONFIGURED && GPIO_IS_VALID_GPIO(gpioNum))
enum class SensorType : byte {
BOILER,
MANUAL,
DS18B20,
BLUETOOTH
};
enum class UnitSystem : byte {
METRIC,
IMPERIAL
};
char buffer[255]; char buffer[255];

View File

@@ -143,7 +143,7 @@ void setup() {
tMqtt = new MqttTask(false, 500); tMqtt = new MqttTask(false, 500);
Scheduler.start(tMqtt); Scheduler.start(tMqtt);
tOt = new OpenThermTask(false, 750); tOt = new OpenThermTask(true, 750);
Scheduler.start(tOt); Scheduler.start(tOt);
tSensors = new SensorsTask(true, EXT_SENSORS_INTERVAL); tSensors = new SensorsTask(true, EXT_SENSORS_INTERVAL);

View File

@@ -1,5 +1,74 @@
#include <Arduino.h> #include <Arduino.h>
inline float liter2gallon(float value) {
return value / 4.546091879f;
}
inline float gallon2liter(float value) {
return value * 4.546091879f;
}
float convertVolume(float value, const UnitSystem unitFrom, const UnitSystem unitTo) {
if (unitFrom == UnitSystem::METRIC && unitTo == UnitSystem::IMPERIAL) {
value = liter2gallon(value);
} else if (unitFrom == UnitSystem::IMPERIAL && unitTo == UnitSystem::METRIC) {
value = gallon2liter(value);
}
return value;
}
inline float bar2psi(float value) {
return value * 14.5038f;
}
inline float psi2bar(float value) {
return value / 14.5038f;
}
float convertPressure(float value, const UnitSystem unitFrom, const UnitSystem unitTo) {
if (unitFrom == UnitSystem::METRIC && unitTo == UnitSystem::IMPERIAL) {
value = bar2psi(value);
} else if (unitFrom == UnitSystem::IMPERIAL && unitTo == UnitSystem::METRIC) {
value = psi2bar(value);
}
return value;
}
inline float c2f(float value) {
return (9.0f / 5.0f) * value + 32.0f;
}
inline float f2c(float value) {
return (value - 32.0f) * (5.0f / 9.0f);
}
float convertTemp(float value, const UnitSystem unitFrom, const UnitSystem unitTo) {
if (unitFrom == UnitSystem::METRIC && unitTo == UnitSystem::IMPERIAL) {
value = c2f(value);
} else if (unitFrom == UnitSystem::IMPERIAL && unitTo == UnitSystem::METRIC) {
value = f2c(value);
}
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;
}
}
double roundd(double value, uint8_t decimals = 2) { double roundd(double value, uint8_t decimals = 2) {
if (decimals == 0) { if (decimals == 0) {
return (int)(value + 0.5); return (int)(value + 0.5);
@@ -13,7 +82,7 @@ double roundd(double value, uint8_t decimals = 2) {
return (int)(value * multiplier) / multiplier; return (int)(value * multiplier) / multiplier;
} }
size_t getTotalHeap() { inline size_t getTotalHeap() {
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
return ESP.getHeapSize(); return ESP.getHeapSize();
#elif defined(ARDUINO_ARCH_ESP8266) #elif defined(ARDUINO_ARCH_ESP8266)
@@ -63,7 +132,7 @@ size_t getMaxFreeBlockHeap(bool getMinValue = false) {
return getMinValue ? minValue : value; return getMinValue ? minValue : value;
} }
uint8_t getHeapFrag() { inline uint8_t getHeapFrag() {
return 100 - getMaxFreeBlockHeap() * 100.0 / getFreeHeap(); return 100 - getMaxFreeBlockHeap() * 100.0 / getFreeHeap();
} }
@@ -268,13 +337,15 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
dst["system"]["debug"] = src.system.debug; dst["system"]["debug"] = src.system.debug;
dst["system"]["useSerial"] = src.system.useSerial; dst["system"]["useSerial"] = src.system.useSerial;
dst["system"]["useTelnet"] = src.system.useTelnet; dst["system"]["useTelnet"] = src.system.useTelnet;
dst["system"]["unitSystem"] = static_cast<byte>(src.system.unitSystem);
dst["portal"]["useAuth"] = src.portal.useAuth; dst["portal"]["useAuth"] = src.portal.useAuth;
dst["portal"]["login"] = src.portal.login; dst["portal"]["login"] = src.portal.login;
dst["portal"]["password"] = src.portal.password; dst["portal"]["password"] = src.portal.password;
dst["opentherm"]["inPin"] = src.opentherm.inPin; dst["opentherm"]["unitSystem"] = static_cast<byte>(src.opentherm.unitSystem);
dst["opentherm"]["outPin"] = src.opentherm.outPin; dst["opentherm"]["inGpio"] = src.opentherm.inGpio;
dst["opentherm"]["outGpio"] = src.opentherm.outGpio;
dst["opentherm"]["memberIdCode"] = src.opentherm.memberIdCode; dst["opentherm"]["memberIdCode"] = src.opentherm.memberIdCode;
dst["opentherm"]["dhwPresent"] = src.opentherm.dhwPresent; dst["opentherm"]["dhwPresent"] = src.opentherm.dhwPresent;
dst["opentherm"]["summerWinterMode"] = src.opentherm.summerWinterMode; dst["opentherm"]["summerWinterMode"] = src.opentherm.summerWinterMode;
@@ -283,6 +354,7 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
dst["opentherm"]["dhwToCh2"] = src.opentherm.dhwToCh2; dst["opentherm"]["dhwToCh2"] = src.opentherm.dhwToCh2;
dst["opentherm"]["dhwBlocking"] = src.opentherm.dhwBlocking; dst["opentherm"]["dhwBlocking"] = src.opentherm.dhwBlocking;
dst["opentherm"]["modulationSyncWithHeating"] = src.opentherm.modulationSyncWithHeating; dst["opentherm"]["modulationSyncWithHeating"] = src.opentherm.modulationSyncWithHeating;
dst["opentherm"]["getMinMaxTemp"] = src.opentherm.getMinMaxTemp;
dst["mqtt"]["server"] = src.mqtt.server; dst["mqtt"]["server"] = src.mqtt.server;
dst["mqtt"]["port"] = src.mqtt.port; dst["mqtt"]["port"] = src.mqtt.port;
@@ -323,12 +395,12 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
dst["equitherm"]["k_factor"] = roundd(src.equitherm.k_factor, 3); dst["equitherm"]["k_factor"] = roundd(src.equitherm.k_factor, 3);
dst["equitherm"]["t_factor"] = roundd(src.equitherm.t_factor, 3); dst["equitherm"]["t_factor"] = roundd(src.equitherm.t_factor, 3);
dst["sensors"]["outdoor"]["type"] = src.sensors.outdoor.type; dst["sensors"]["outdoor"]["type"] = static_cast<byte>(src.sensors.outdoor.type);
dst["sensors"]["outdoor"]["pin"] = src.sensors.outdoor.pin; dst["sensors"]["outdoor"]["gpio"] = src.sensors.outdoor.gpio;
dst["sensors"]["outdoor"]["offset"] = roundd(src.sensors.outdoor.offset, 2); dst["sensors"]["outdoor"]["offset"] = roundd(src.sensors.outdoor.offset, 2);
dst["sensors"]["indoor"]["type"] = src.sensors.indoor.type; dst["sensors"]["indoor"]["type"] = static_cast<byte>(src.sensors.indoor.type);
dst["sensors"]["indoor"]["pin"] = src.sensors.indoor.pin; dst["sensors"]["indoor"]["gpio"] = src.sensors.indoor.gpio;
char bleAddress[18]; char bleAddress[18];
sprintf( sprintf(
@@ -346,7 +418,7 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
if (!safe) { if (!safe) {
dst["externalPump"]["use"] = src.externalPump.use; dst["externalPump"]["use"] = src.externalPump.use;
dst["externalPump"]["pin"] = src.externalPump.pin; dst["externalPump"]["gpio"] = src.externalPump.gpio;
dst["externalPump"]["postCirculationTime"] = roundd(src.externalPump.postCirculationTime / 60, 0); dst["externalPump"]["postCirculationTime"] = roundd(src.externalPump.postCirculationTime / 60, 0);
dst["externalPump"]["antiStuckInterval"] = roundd(src.externalPump.antiStuckInterval / 86400, 0); dst["externalPump"]["antiStuckInterval"] = roundd(src.externalPump.antiStuckInterval / 86400, 0);
dst["externalPump"]["antiStuckTime"] = roundd(src.externalPump.antiStuckTime / 60, 0); dst["externalPump"]["antiStuckTime"] = roundd(src.externalPump.antiStuckTime / 60, 0);
@@ -377,6 +449,39 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
changed = true; changed = true;
} }
if (!src["system"]["unitSystem"].isNull()) {
byte value = src["system"]["unitSystem"].as<unsigned char>();
UnitSystem prevUnitSystem = dst.system.unitSystem;
switch (value) {
case static_cast<byte>(UnitSystem::METRIC):
dst.system.unitSystem = UnitSystem::METRIC;
changed = true;
break;
case static_cast<byte>(UnitSystem::IMPERIAL):
dst.system.unitSystem = UnitSystem::IMPERIAL;
changed = true;
break;
default:
break;
}
// convert temps
if (dst.system.unitSystem != prevUnitSystem) {
dst.emergency.target = convertTemp(dst.emergency.target, prevUnitSystem, dst.system.unitSystem);
dst.heating.target = convertTemp(dst.heating.target, prevUnitSystem, dst.system.unitSystem);
dst.heating.minTemp = convertTemp(dst.heating.minTemp, prevUnitSystem, dst.system.unitSystem);
dst.heating.maxTemp = convertTemp(dst.heating.maxTemp, prevUnitSystem, dst.system.unitSystem);
dst.dhw.target = convertTemp(dst.dhw.target, prevUnitSystem, dst.system.unitSystem);
dst.dhw.minTemp = convertTemp(dst.dhw.minTemp, prevUnitSystem, dst.system.unitSystem);
dst.dhw.maxTemp = convertTemp(dst.dhw.maxTemp, prevUnitSystem, dst.system.unitSystem);
dst.pid.minTemp = convertTemp(dst.pid.minTemp, prevUnitSystem, dst.system.unitSystem);
dst.pid.maxTemp = convertTemp(dst.pid.maxTemp, prevUnitSystem, dst.system.unitSystem);
}
}
// portal // portal
if (src["portal"]["useAuth"].is<bool>()) { if (src["portal"]["useAuth"].is<bool>()) {
@@ -404,21 +509,56 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
// opentherm // opentherm
if (!src["opentherm"]["inPin"].isNull()) { if (!src["opentherm"]["unitSystem"].isNull()) {
unsigned char value = src["opentherm"]["inPin"].as<unsigned char>(); byte value = src["opentherm"]["unitSystem"].as<unsigned char>();
if (value >= 0 && value < 50) { switch (value) {
dst.opentherm.inPin = value; case static_cast<byte>(UnitSystem::METRIC):
changed = true; dst.opentherm.unitSystem = UnitSystem::METRIC;
changed = true;
break;
case static_cast<byte>(UnitSystem::IMPERIAL):
dst.opentherm.unitSystem = UnitSystem::IMPERIAL;
changed = true;
break;
default:
break;
} }
} }
if (!src["opentherm"]["outPin"].isNull()) { if (!src["opentherm"]["inGpio"].isNull()) {
unsigned char value = src["opentherm"]["outPin"].as<unsigned char>(); if (src["opentherm"]["inGpio"].is<JsonString>() && src["opentherm"]["inGpio"].as<JsonString>().size() == 0) {
if (dst.opentherm.inGpio != GPIO_IS_NOT_CONFIGURED) {
dst.opentherm.inGpio = GPIO_IS_NOT_CONFIGURED;
changed = true;
}
if (value >= 0 && value < 50) { } else {
dst.opentherm.outPin = value; unsigned char value = src["opentherm"]["inGpio"].as<unsigned char>();
changed = true;
if (value >= 0 && value <= 254) {
dst.opentherm.inGpio = value;
changed = true;
}
}
}
if (!src["opentherm"]["outGpio"].isNull()) {
if (src["opentherm"]["outGpio"].is<JsonString>() && src["opentherm"]["outGpio"].as<JsonString>().size() == 0) {
if (dst.opentherm.outGpio != GPIO_IS_NOT_CONFIGURED) {
dst.opentherm.outGpio = GPIO_IS_NOT_CONFIGURED;
changed = true;
}
} else {
unsigned char value = src["opentherm"]["outGpio"].as<unsigned char>();
if (value >= 0 && value <= 254) {
dst.opentherm.outGpio = value;
changed = true;
}
} }
} }
@@ -484,6 +624,11 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
changed = true; changed = true;
} }
if (src["opentherm"]["getMinMaxTemp"].is<bool>()) {
dst.opentherm.getMinMaxTemp = src["opentherm"]["getMinMaxTemp"].as<bool>();
changed = true;
}
// mqtt // mqtt
if (!src["mqtt"]["server"].isNull()) { if (!src["mqtt"]["server"].isNull()) {
@@ -551,14 +696,14 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (!src["emergency"]["target"].isNull()) { if (!src["emergency"]["target"].isNull()) {
double value = src["emergency"]["target"].as<double>(); double value = src["emergency"]["target"].as<double>();
if (value > 0 && value < 100) { if (isValidTemp(value, dst.system.unitSystem)) {
dst.emergency.target = roundd(value, 2); dst.emergency.target = roundd(value, 2);
changed = true; changed = true;
} }
} }
if (src["emergency"]["useEquitherm"].is<bool>()) { if (src["emergency"]["useEquitherm"].is<bool>()) {
if (dst.sensors.outdoor.type != 1) { if (dst.sensors.outdoor.type != SensorType::MANUAL) {
dst.emergency.useEquitherm = src["emergency"]["useEquitherm"].as<bool>(); dst.emergency.useEquitherm = src["emergency"]["useEquitherm"].as<bool>();
} else { } else {
@@ -573,7 +718,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
} }
if (src["emergency"]["usePid"].is<bool>()) { if (src["emergency"]["usePid"].is<bool>()) {
if (dst.sensors.indoor.type != 1) { if (dst.sensors.indoor.type != SensorType::MANUAL) {
dst.emergency.usePid = src["emergency"]["usePid"].as<bool>(); dst.emergency.usePid = src["emergency"]["usePid"].as<bool>();
} else { } else {
@@ -602,7 +747,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (!src["heating"]["target"].isNull()) { if (!src["heating"]["target"].isNull()) {
double value = src["heating"]["target"].as<double>(); double value = src["heating"]["target"].as<double>();
if (value > 0 && value < 100) { if (isValidTemp(value, dst.system.unitSystem)) {
dst.heating.target = roundd(value, 2); dst.heating.target = roundd(value, 2);
changed = true; changed = true;
} }
@@ -654,7 +799,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (!src["dhw"]["target"].isNull()) { if (!src["dhw"]["target"].isNull()) {
unsigned char value = src["dhw"]["target"].as<unsigned char>(); unsigned char value = src["dhw"]["target"].as<unsigned char>();
if (value >= 0 && value < 100) { if (isValidTemp(value, dst.system.unitSystem)) {
dst.dhw.target = value; dst.dhw.target = value;
changed = true; changed = true;
} }
@@ -724,7 +869,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (!src["pid"]["maxTemp"].isNull()) { if (!src["pid"]["maxTemp"].isNull()) {
unsigned char value = src["pid"]["maxTemp"].as<unsigned char>(); unsigned char value = src["pid"]["maxTemp"].as<unsigned char>();
if (value > 0 && value <= 100 && value > dst.pid.minTemp) { if (isValidTemp(value, dst.system.unitSystem) && value > dst.pid.minTemp) {
dst.pid.maxTemp = value; dst.pid.maxTemp = value;
changed = true; changed = true;
} }
@@ -733,7 +878,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (!src["pid"]["minTemp"].isNull()) { if (!src["pid"]["minTemp"].isNull()) {
unsigned char value = src["pid"]["minTemp"].as<unsigned char>(); unsigned char value = src["pid"]["minTemp"].as<unsigned char>();
if (value >= 0 && value < 100 && value < dst.pid.maxTemp) { if (isValidTemp(value, dst.system.unitSystem) && value < dst.pid.maxTemp) {
dst.pid.minTemp = value; dst.pid.minTemp = value;
changed = true; changed = true;
} }
@@ -776,25 +921,44 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
// sensors // sensors
if (!src["sensors"]["outdoor"]["type"].isNull()) { if (!src["sensors"]["outdoor"]["type"].isNull()) {
unsigned char value = src["sensors"]["outdoor"]["type"].as<unsigned char>(); byte value = src["sensors"]["outdoor"]["type"].as<unsigned char>();
if (value >= 0 && value <= 2) { switch (value) {
dst.sensors.outdoor.type = value; case static_cast<byte>(SensorType::BOILER):
dst.sensors.outdoor.type = SensorType::BOILER;
changed = true;
break;
if (dst.sensors.outdoor.type == 1) { case static_cast<byte>(SensorType::MANUAL):
dst.sensors.outdoor.type = SensorType::MANUAL;
dst.emergency.useEquitherm = false; dst.emergency.useEquitherm = false;
} changed = true;
break;
changed = true; case static_cast<byte>(SensorType::DS18B20):
dst.sensors.outdoor.type = SensorType::DS18B20;
changed = true;
break;
default:
break;
} }
} }
if (!src["sensors"]["outdoor"]["pin"].isNull()) { if (!src["sensors"]["outdoor"]["gpio"].isNull()) {
unsigned char value = src["sensors"]["outdoor"]["pin"].as<unsigned char>(); if (src["sensors"]["outdoor"]["gpio"].is<JsonString>() && src["sensors"]["outdoor"]["gpio"].as<JsonString>().size() == 0) {
if (dst.sensors.outdoor.gpio != GPIO_IS_NOT_CONFIGURED) {
dst.sensors.outdoor.gpio = GPIO_IS_NOT_CONFIGURED;
changed = true;
}
if (value >= 0 && value <= 50) { } else {
dst.sensors.outdoor.pin = value; unsigned char value = src["sensors"]["outdoor"]["gpio"].as<unsigned char>();
changed = true;
if (value >= 0 && value <= 254) {
dst.sensors.outdoor.gpio = value;
changed = true;
}
} }
} }
@@ -808,25 +972,52 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
} }
if (!src["sensors"]["indoor"]["type"].isNull()) { if (!src["sensors"]["indoor"]["type"].isNull()) {
unsigned char value = src["sensors"]["indoor"]["type"].as<unsigned char>(); byte value = src["sensors"]["indoor"]["type"].as<unsigned char>();
if (value >= 1 && value <= 3) {
dst.sensors.indoor.type = value;
if (dst.sensors.indoor.type == 1) { switch (value) {
dst.emergency.usePid = false; case static_cast<byte>(SensorType::BOILER):
} dst.sensors.indoor.type = SensorType::BOILER;
changed = true;
break;
changed = true; case static_cast<byte>(SensorType::MANUAL):
dst.sensors.indoor.type = SensorType::MANUAL;
dst.emergency.useEquitherm = false;
changed = true;
break;
case static_cast<byte>(SensorType::DS18B20):
dst.sensors.indoor.type = SensorType::DS18B20;
changed = true;
break;
#if USE_BLE
case static_cast<byte>(SensorType::BLUETOOTH):
dst.sensors.indoor.type = SensorType::BLUETOOTH;
changed = true;
break;
#endif
default:
break;
} }
} }
if (!src["sensors"]["indoor"]["pin"].isNull()) { if (!src["sensors"]["indoor"]["gpio"].isNull()) {
unsigned char value = src["sensors"]["indoor"]["pin"].as<unsigned char>(); if (src["sensors"]["indoor"]["gpio"].is<JsonString>() && src["sensors"]["indoor"]["gpio"].as<JsonString>().size() == 0) {
if (dst.sensors.indoor.gpio != GPIO_IS_NOT_CONFIGURED) {
dst.sensors.indoor.gpio = GPIO_IS_NOT_CONFIGURED;
changed = true;
}
if (value >= 0 && value <= 50) { } else {
dst.sensors.indoor.pin = value; unsigned char value = src["sensors"]["indoor"]["gpio"].as<unsigned char>();
changed = true;
if (value >= 0 && value <= 254) {
dst.sensors.indoor.gpio = value;
changed = true;
}
} }
} }
@@ -861,12 +1052,20 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
changed = true; changed = true;
} }
if (!src["externalPump"]["pin"].isNull()) { if (!src["externalPump"]["gpio"].isNull()) {
unsigned char value = src["externalPump"]["pin"].as<unsigned char>(); if (src["externalPump"]["gpio"].is<JsonString>() && src["externalPump"]["gpio"].as<JsonString>().size() == 0) {
if (dst.externalPump.gpio != GPIO_IS_NOT_CONFIGURED) {
dst.externalPump.gpio = GPIO_IS_NOT_CONFIGURED;
changed = true;
}
if (value >= 0 && value <= 50) { } else {
dst.externalPump.pin = value; unsigned char value = src["externalPump"]["gpio"].as<unsigned char>();
changed = true;
if (value >= 0 && value <= 254) {
dst.externalPump.gpio = value;
changed = true;
}
} }
} }
@@ -928,7 +1127,9 @@ void varsToJson(const Variables& src, JsonVariant dst) {
dst["temperatures"]["indoor"] = roundd(src.temperatures.indoor, 2); dst["temperatures"]["indoor"] = roundd(src.temperatures.indoor, 2);
dst["temperatures"]["outdoor"] = roundd(src.temperatures.outdoor, 2); dst["temperatures"]["outdoor"] = roundd(src.temperatures.outdoor, 2);
dst["temperatures"]["heating"] = roundd(src.temperatures.heating, 2); dst["temperatures"]["heating"] = roundd(src.temperatures.heating, 2);
dst["temperatures"]["heatingReturn"] = roundd(src.temperatures.heatingReturn, 2);
dst["temperatures"]["dhw"] = roundd(src.temperatures.dhw, 2); dst["temperatures"]["dhw"] = roundd(src.temperatures.dhw, 2);
dst["temperatures"]["exhaust"] = roundd(src.temperatures.exhaust, 2);
dst["parameters"]["heatingEnabled"] = src.parameters.heatingEnabled; dst["parameters"]["heatingEnabled"] = src.parameters.heatingEnabled;
dst["parameters"]["heatingMinTemp"] = src.parameters.heatingMinTemp; dst["parameters"]["heatingMinTemp"] = src.parameters.heatingMinTemp;
@@ -961,7 +1162,7 @@ bool jsonToVars(const JsonVariantConst src, Variables& dst) {
if (!src["temperatures"]["indoor"].isNull()) { if (!src["temperatures"]["indoor"].isNull()) {
double value = src["temperatures"]["indoor"].as<double>(); double value = src["temperatures"]["indoor"].as<double>();
if (settings.sensors.indoor.type == 1 && value > -100 && value < 100) { if (settings.sensors.indoor.type == SensorType::MANUAL && isValidTemp(value, settings.system.unitSystem, -99.9f, 99.9f)) {
dst.temperatures.indoor = roundd(value, 2); dst.temperatures.indoor = roundd(value, 2);
changed = true; changed = true;
} }
@@ -970,7 +1171,7 @@ bool jsonToVars(const JsonVariantConst src, Variables& dst) {
if (!src["temperatures"]["outdoor"].isNull()) { if (!src["temperatures"]["outdoor"].isNull()) {
double value = src["temperatures"]["outdoor"].as<double>(); double value = src["temperatures"]["outdoor"].as<double>();
if (settings.sensors.outdoor.type == 1 && value > -100 && value < 100) { if (settings.sensors.outdoor.type == SensorType::MANUAL && isValidTemp(value, settings.system.unitSystem, -99.9f, 99.9f)) {
dst.temperatures.outdoor = roundd(value, 2); dst.temperatures.outdoor = roundd(value, 2);
changed = true; changed = true;
} }

View File

@@ -484,6 +484,7 @@ async function loadSettings() {
setCheckboxValue('.system-debug', result.system.debug); setCheckboxValue('.system-debug', result.system.debug);
setCheckboxValue('.system-use-serial', result.system.useSerial); setCheckboxValue('.system-use-serial', result.system.useSerial);
setCheckboxValue('.system-use-telnet', result.system.useTelnet); setCheckboxValue('.system-use-telnet', result.system.useTelnet);
setRadioValue('.system-unit-system', result.system.unitSystem);
setBusy('#system-settings-busy', '#system-settings', false); setBusy('#system-settings-busy', '#system-settings', false);
setCheckboxValue('.portal-use-auth', result.portal.useAuth); setCheckboxValue('.portal-use-auth', result.portal.useAuth);
@@ -491,8 +492,9 @@ async function loadSettings() {
setInputValue('.portal-password', result.portal.password); setInputValue('.portal-password', result.portal.password);
setBusy('#portal-settings-busy', '#portal-settings', false); setBusy('#portal-settings-busy', '#portal-settings', false);
setInputValue('.opentherm-in-pin', result.opentherm.inPin); setRadioValue('.opentherm-unit-system', result.opentherm.unitSystem);
setInputValue('.opentherm-out-pin', result.opentherm.outPin); setInputValue('.opentherm-in-gpio', result.opentherm.inGpio < 255 ? result.opentherm.inGpio : '');
setInputValue('.opentherm-out-gpio', result.opentherm.outGpio < 255 ? result.opentherm.outGpio : '');
setInputValue('.opentherm-member-id-code', result.opentherm.memberIdCode); setInputValue('.opentherm-member-id-code', result.opentherm.memberIdCode);
setCheckboxValue('.opentherm-dhw-present', result.opentherm.dhwPresent); setCheckboxValue('.opentherm-dhw-present', result.opentherm.dhwPresent);
setCheckboxValue('.opentherm-sw-mode', result.opentherm.summerWinterMode); setCheckboxValue('.opentherm-sw-mode', result.opentherm.summerWinterMode);
@@ -501,6 +503,7 @@ async function loadSettings() {
setCheckboxValue('.opentherm-dhw-to-ch2', result.opentherm.dhwToCh2); setCheckboxValue('.opentherm-dhw-to-ch2', result.opentherm.dhwToCh2);
setCheckboxValue('.opentherm-dhw-blocking', result.opentherm.dhwBlocking); setCheckboxValue('.opentherm-dhw-blocking', result.opentherm.dhwBlocking);
setCheckboxValue('.opentherm-sync-modulation-with-heating', result.opentherm.modulationSyncWithHeating); setCheckboxValue('.opentherm-sync-modulation-with-heating', result.opentherm.modulationSyncWithHeating);
setCheckboxValue('.opentherm-get-min-max-temp', result.opentherm.getMinMaxTemp);
setBusy('#opentherm-settings-busy', '#opentherm-settings', false); setBusy('#opentherm-settings-busy', '#opentherm-settings', false);
setInputValue('.mqtt-server', result.mqtt.server); setInputValue('.mqtt-server', result.mqtt.server);
@@ -512,18 +515,18 @@ async function loadSettings() {
setBusy('#mqtt-settings-busy', '#mqtt-settings', false); setBusy('#mqtt-settings-busy', '#mqtt-settings', false);
setRadioValue('.outdoor-sensor-type', result.sensors.outdoor.type); setRadioValue('.outdoor-sensor-type', result.sensors.outdoor.type);
setInputValue('.outdoor-sensor-pin', result.sensors.outdoor.pin); setInputValue('.outdoor-sensor-gpio', result.sensors.outdoor.gpio < 255 ? result.sensors.outdoor.gpio : '');
setInputValue('.outdoor-sensor-offset', result.sensors.outdoor.offset); setInputValue('.outdoor-sensor-offset', result.sensors.outdoor.offset);
setBusy('#outdoor-sensor-settings-busy', '#outdoor-sensor-settings', false); setBusy('#outdoor-sensor-settings-busy', '#outdoor-sensor-settings', false);
setRadioValue('.indoor-sensor-type', result.sensors.indoor.type); setRadioValue('.indoor-sensor-type', result.sensors.indoor.type);
setInputValue('.indoor-sensor-pin', result.sensors.indoor.pin); setInputValue('.indoor-sensor-gpio', result.sensors.indoor.gpio < 255 ? result.sensors.indoor.gpio : '');
setInputValue('.indoor-sensor-offset', result.sensors.indoor.offset); setInputValue('.indoor-sensor-offset', result.sensors.indoor.offset);
setInputValue('.indoor-sensor-ble-addresss', result.sensors.indoor.bleAddresss); setInputValue('.indoor-sensor-ble-addresss', result.sensors.indoor.bleAddresss);
setBusy('#indoor-sensor-settings-busy', '#indoor-sensor-settings', false); setBusy('#indoor-sensor-settings-busy', '#indoor-sensor-settings', false);
setCheckboxValue('.extpump-use', result.externalPump.use); setCheckboxValue('.extpump-use', result.externalPump.use);
setInputValue('.extpump-pin', result.externalPump.pin); setInputValue('.extpump-gpio', result.externalPump.gpio < 255 ? result.externalPump.gpio : '');
setInputValue('.extpump-pc-time', result.externalPump.postCirculationTime); setInputValue('.extpump-pc-time', result.externalPump.postCirculationTime);
setInputValue('.extpump-as-interval', result.externalPump.antiStuckInterval); setInputValue('.extpump-as-interval', result.externalPump.antiStuckInterval);
setInputValue('.extpump-as-time', result.externalPump.antiStuckTime); setInputValue('.extpump-as-time', result.externalPump.antiStuckTime);
@@ -534,6 +537,27 @@ async function loadVars() {
let response = await fetch('/api/vars'); let response = await fetch('/api/vars');
let result = await response.json(); let result = await response.json();
let tempUnitStr, pressureUnitStr, volumeUnitStr;
switch (result.system.unitSystem) {
case 0:
tempUnitStr = 'C';
pressureUnitStr = 'bar';
volumeUnitStr = 'L';
break;
case 1:
tempUnitStr = 'F';
pressureUnitStr = 'psi';
volumeUnitStr = 'gal';
break;
default:
tempUnitStr = '?';
pressureUnitStr = '?';
volumeUnitStr = '?';
break;
}
setState('.ot-connected', result.states.otStatus); setState('.ot-connected', result.states.otStatus);
setState('.ot-emergency', result.states.emergency); setState('.ot-emergency', result.states.emergency);
setState('.ot-heating', result.states.heating); setState('.ot-heating', result.states.heating);
@@ -552,10 +576,15 @@ async function loadVars() {
setValue('.outdoor-temp', result.temperatures.outdoor); setValue('.outdoor-temp', result.temperatures.outdoor);
setValue('.heating-temp', result.temperatures.heating); setValue('.heating-temp', result.temperatures.heating);
setValue('.heating-setpoint-temp', result.parameters.heatingSetpoint); setValue('.heating-setpoint-temp', result.parameters.heatingSetpoint);
setValue('.heating-return-temp', result.temperatures.heatingReturn);
setValue('.dhw-temp', result.temperatures.dhw); setValue('.dhw-temp', result.temperatures.dhw);
setValue('.exhaust-temp', result.temperatures.exhaust);
setBusy('.ot-busy', '.ot-table', false); setBusy('.ot-busy', '.ot-table', false);
setValue('.temp-unit', tempUnitStr);
setValue('.pressure-unit', pressureUnitStr);
setValue('.volume-unit', volumeUnitStr);
setValue('.version', result.system.version); setValue('.version', result.system.version);
setValue('.build-date', result.system.buildDate); setValue('.build-date', result.system.buildDate);
setValue('.uptime', result.system.uptime); setValue('.uptime', result.system.uptime);
@@ -601,12 +630,14 @@ function setState(selector, value) {
} }
function setValue(selector, value) { function setValue(selector, value) {
let item = document.querySelector(selector); let items = document.querySelectorAll(selector);
if (!item) { if (!items.length) {
return; return;
} }
item.innerHTML = value; for (let item of items) {
item.innerHTML = value;
}
} }
function setCheckboxValue(selector, value) { function setCheckboxValue(selector, value) {