27 Commits
1.2.1 ... 1.3.2

Author SHA1 Message Date
Yurii
5c0dfc544e update to 1.3.2 2023-10-21 03:02:38 +03:00
Yurii
5fba94312b added build functions 2023-10-21 03:01:48 +03:00
Yurii
62bea87f8a upd env 2023-10-21 01:48:44 +03:00
Yurii
f52aa8e889 removed unused code 2023-10-20 21:07:12 +03:00
Yurii
df8354866f fix entity climate.heating 2023-10-20 21:06:44 +03:00
Yurii
6242db7a29 removed unused code 2023-10-19 02:20:37 +03:00
Yurii
dc00fdcdb6 Fix Error ''max' must be > 'min'' when processing MQTT 2023-10-19 02:18:39 +03:00
Yurii
2615e9106e upd readme 2023-10-19 00:45:06 +03:00
Yurii
0f60a07a71 upd readme 2023-10-19 00:43:17 +03:00
Yurii
f8750373d4 format code 2023-10-19 00:40:07 +03:00
Yurii
d5a92c47c7 Fixed get current dhw temp 2023-10-19 00:34:11 +03:00
Yurii
bc91168bbf added interval for forced set temperatures 2023-10-17 14:50:43 +03:00
Yurii
96c1a187cd upd readme, get modulation fix 2023-10-17 14:14:24 +03:00
Yurii
6d3172b73b fix pid 2023-10-11 19:04:58 +03:00
Yurii
fca6dc9393 fix set hysteresis 2023-10-11 18:46:37 +03:00
Yurii
b54ea9b745 dependency update (temporary) 2023-10-09 06:59:41 +03:00
Yurii
5de3238f6f Added DHW present switch 2023-10-09 06:20:55 +03:00
Yurii
2270b12b36 upd readme 2023-09-22 00:20:45 +03:00
Yurii
fd4fd119da upd readme 2023-09-21 23:47:43 +03:00
Yurii
47849eab01 upd readme & pcb 2023-09-21 23:09:47 +03:00
Yurii
ef99d2af96 upd readme 2023-09-21 23:02:04 +03:00
Yurii
826581562a upd readme, small fix 2023-09-21 22:51:14 +03:00
Yurii
d10d44bd13 upd pcb 2023-09-21 06:23:26 +03:00
Yurii
229628fdc5 Many changes.
1. Migrate from microDS18B20 to DallasTemperature
2. Refactoring of sensors: added an external temperature sensor inside the house, added an "offset" parameter for sensors
3. Fixed PID
4. New parameters added:
- settings.heating.minTemp
- settings.heating.maxTemp
- settings.dhw.minTemp
- settings.dhw.maxTemp
- settings.pid.minTemp
- settings.pid.maxTemp
- settings.sensors.outdoor.type
- settings.sensors.outdoor.pin
- settings.sensors.outdoor.offset
- settings.sensors.indoor.type
- settings.sensors.indoor.pin
- settings.sensors.indoor.offset
5. Fixed and updated HomeAssistantHelper
7. Added check for validity of settings. After some updates, the settings may be reset to default, but this will prevent the settings from being distorted.
2023-09-21 05:18:05 +03:00
Laxilef
b0e01afecb Update README.md 2023-09-19 22:01:40 +03:00
Yurii
f544baee0a Merge branch 'master' of https://github.com/Laxilef/OTGateway 2023-09-19 21:59:46 +03:00
Yurii
3ff68f544f upd readme
upd gitignore
add ino file for arduino ide
2023-09-19 21:59:29 +03:00
23 changed files with 2803 additions and 1755 deletions

6
.gitignore vendored
View File

@@ -1,5 +1,3 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.vscode
build/*

105
README.md
View File

@@ -7,12 +7,12 @@
- PID
- Equithermic curves - adjusts the temperature based on indoor and outdoor temperatures
- Hysteresis setting (for accurate maintenance of room temperature)
- Ability to connect an external sensor to monitor outdoor temperature (DS18B20)
- Ability to connect an external sensors to monitor outdoor and indoor temperature ([compatible sensors](#compatible-temperature-sensors))
- Emergency mode. If the Wi-Fi connection is lost or the gateway cannot connect to the MQTT server, the mode will turn on. This mode will automatically maintain the set temperature and prevent your home from freezing. In this mode it is also possible to use equithermal curves (weather-compensated control).
- Automatic error reset (not with all boilers)
- Diagnostics:
- The process of heating the coolant for heating: works / does not work
- The process of heating water for hot water: working / not working
- The process of heating the coolant for heating: works/does not work
- The process of heating water for hot water: working/not working
- Display of boiler errors
- Burner status: on/off
- Burner modulation level in percent
@@ -28,52 +28,58 @@
![logo](/assets/ha.png)
## Tested on
| Boiler | Master Member ID |
| --- | --- |
| BAXI ECO Nova | default or 4 |
| BAXI Ampera | 1028 |
| Boiler | Master Member ID | Notes |
| --- | --- | --- |
| BAXI ECO Nova | default | Pressure sensor not supported, modulation level not stable |
| BAXI Ampera | 1028 | Pressure sensor not supported, only heating (DHW not tested) |
| [Remeha Calenta Ace 40C](https://github.com/Laxilef/OTGateway/issues/1#issuecomment-1726081554) | default | - |
| [Baxi Nuvola DUO-TEC HT 16](https://github.com/Laxilef/OTGateway/issues/3#issuecomment-1751061488) | default | - |
| [AEG GBA124](https://github.com/Laxilef/OTGateway/issues/3#issuecomment-1765857609) | default | Pressure sensor not supported |
| [Ferroli DOMIcompact C 24](https://github.com/Laxilef/OTGateway/issues/3#issuecomment-1765310058)<br><sub>Board: MF08FA</sub> | 211 | Pressure sensor not supported |
| [Thermet Ecocondens Silver 35kW)](https://github.com/Laxilef/OTGateway/issues/3#issuecomment-1767026384) | default | Pressure sensor not supported |
## PCB
<img src="/assets/pcb.svg" width="25%" /> <img src="/assets/pcb_3d.png" width="30%" /> <img src="/assets/after_assembly.png" width="37%" />
<img src="/assets/pcb.svg" width="27%" /> <img src="/assets/pcb_3d.png" width="30%" /> <img src="/assets/after_assembly.png" width="40%" />
Housing for installation on DIN rail - D2MG. Occupies only 2 DIN modules.<br>
The 220V > 5V power supply is already on the board, so additional power supplies are not needed.<br>
To save money, 2 levels are ordered as one board. After manufacturing, the boards need to be divided into 2 parts - upper and lower.<br>
**Important!** On this board opentherm IN pin = 5, OUT pin = 4
To save money, 2 levels are ordered as one board. After manufacturing, the boards need to be divided into 2 parts - upper and lower. The boards are inexpensively (5pcs for $2) manufactured at JLCPCB (Remove Order Number = Specify a location).<br><br>
Some components can be replaced with similar ones (for example use a fuse and led with legs). Some SMD components (for example optocouplers) can be replaced with similar SOT components.<br>Most of the components can be purchased inexpensively on Aliexpress, the rest in your local stores.<br><br>
The outdoor temperature sensor must be connected to the **TEMP1** connector, the indoor temperature sensor must be connected to the **TEMP2** connector. The power supply for the sensors must be connected to the **3.3V** connector, GND to **GND**.<br>
**The opentherm connection polarity does not matter.**
<!-- **Important!** On this board opentherm IN pin = 5, OUT pin = 4 -->
- [Sheet](/assets/sheet.pdf)
- [Schematic](/assets/Schematic.pdf)
- [BOM](/assets/BOM.xlsx)
- [Gerber](/assets/gerber.zip)
## Another compatible Open Therm Adapters
## Another compatible OpenTherm Adapters
- [Ihor Melnyk OpenTherm Adapter](http://ihormelnyk.com/opentherm_adapter)
- [DIYLESS Master OpenTherm Shield](https://diyless.com/product/master-opentherm-shield)
- [OpenTherm master shield for Wemos/Lolin](https://www.tindie.com/products/thehognl/opentherm-master-shield-for-wemoslolin/)
- And others. It's just that the adapter must implement [the schema](http://ihormelnyk.com/Content/Pages/opentherm_adapter/opentherm_adapter_schematic_o.png)
## Compatible Temperature Sensors
* DS18B20
* DS1822
* DS1820
* MAX31820
* MAX31850
[See more](https://github.com/milesburton/Arduino-Temperature-Control-Library#usage)
# Quick Start
## Dependencies
- [ESP8266Scheduler](https://github.com/nrwiersma/ESP8266Scheduler)
- [NTPClient](https://github.com/arduino-libraries/NTPClient)
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
- [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library)
- [PubSubClient](https://github.com/knolleary/pubsubclient)
- [TelnetStream](https://github.com/jandrassy/TelnetStream)
- [EEManager](https://github.com/GyverLibs/EEManager)
- [GyverPID](https://github.com/GyverLibs/GyverPID)
- [microDS18B20](https://github.com/GyverLibs/microDS18B20)
- [WiFiManager](https://github.com/tzapu/WiFiManager)
1. Download the latest firmware from the [releases page](https://github.com/Laxilef/OTGateway/releases) (or compile yourself) and flash your ESP8266 board using the [ESP Flash Download Tool](https://www.espressif.com/en/support/download/other-tools) or other software.
2. Connect to *OpenTherm Gateway* hotspot, password: otgateway123456
3. Open configuration page in browser: 192.168.4.1
4. Set up a connection to your wifi network
5. Set up a connection to your MQTT server: ip, port, user, password
6. Set up a **Opentherm pin IN** & **Opentherm pin OUT**. No change for my board. Typically used **IN** = 4, **OUT** = 5
7. Set up a **Outdoor sensor pin** & **Indoor sensor pin**. No change for my board.
8. if necessary, set up a the master member ID ([see more](#tested-on))
9. Restart module (required after changing OT pins and/or sensors pins!)
## Settings
1. Connect to *OpenTherm Gateway* hotspot, password: otgateway123456
2. Open configuration page in browser: 192.168.4.1
3. Set up a connection to your wifi network
4. Set up a connection to your MQTT server
5. Set up a **Opentherm pin IN** & **Opentherm pin OUT**. Typically used **IN** = 4, **OUT** = 5
6. if necessary, set the master member ID.
7. Restart module (required after changing OT pins!)
After connecting to your wifi network, you can go to the setup page at the address that esp8266 received.
After connecting to your wifi network, you can go to the setup page at the address that ESP8266 received.
The OTGateway device will be automatically added to homeassistant if MQTT server ip, login and password are correct.
## HomeAsssistant settings
@@ -109,7 +115,7 @@ The temperature inside the house can be set using simple automation:
```
</details>
If your boiler does not support the installation of an outdoor temperature sensor or does not provide this value via the opentherm protocol, then you can use an external DS18B20 sensor or use automation.
If your boiler does not support the installation of an outdoor temperature sensor or does not provide this value via the opentherm protocol, then you can use an external sensor or use automation.
<details>
<summary>Simple automation</summary>
@@ -166,13 +172,16 @@ Range: 0...10, default: 2, step 0.01
#### Instructions for fit coefficients:
**Tip.** I created a [table in Excel](/assets/equitherm_calc.xlsx) in which you can enter temperature parameters inside and outside the house and select coefficients. On the graph you can see the temperature that the boiler will set.
1. The first thing you need to do is to fit the curve (***N*** coefficient). If your home has low heat loss, then start with 0.5. Otherwise start at 0.7. When the temperature inside the house stops changing, increase or decrease the coefficient value in increments of 0.1 to select the optimal curve.<br>
1. Set the ***K*** and ***T*** coefficients to 0.
2. The first thing you need to do is to fit the curve (***N*** coefficient). If your home has low heat loss, then start with 0.5. Otherwise start at 0.7. When the temperature inside the house stops changing, increase or decrease the coefficient value in increments of 0.1 to select the optimal curve.<br>
Please note that passive heating (sun) will affect the house temperature during curve fitting. This process is not fast and will take you 1-2 days.
Important. During curve fitting, the temperature must be kept stable as the outside temperature changes. The temperature does not have to be equal to the set one.<br>
For example. You fit curve 0.67; set temperature 23; the temperature in the house is 20 degrees while the outside temperature is -10 degrees and -5 degrees. This is good.
2. After fitting the curve, you must select the ***K*** coefficient. It influences the boiler temperature correction to maintain the set temperature.
For example. Set temperature: 23 degrees; temperature in the house: 20 degrees. Try setting it to 5 and see how the temperature in the house changes after stabilization. Select the value so that the temperature in the house is close to the set.
3. Now you can choose the ***T*** coefficient. Simply put, it affects the sharpness of the temperature change. If you want fast heating, then set a high value (6-10), but then the room may overheat. If you want smooth heating, set 1-5. Choose the optimal value for yourself.
Important. During curve fitting, the temperature must be kept stable as the outside temperature changes.<br>
At this stage, it is important for you to stabilize the indoor temperature at exactly 20 (+- 0.5) degrees.<br>
For example. You fit curve 0.67; set temperature 20; the temperature in the house is 20.1 degrees while the outside temperature is -10 degrees and -5 degrees. This is good.
3. After fitting the curve, you must select the ***K*** coefficient. It influences the boiler temperature correction to maintain the set temperature.
For example. Set temperature: 23 degrees; temperature in the house: 20 degrees. Try setting it to 2 and see how the temperature in the house changes after stabilization. Select the value so that the temperature in the house is close to the set.
4. Now you can choose the ***T*** coefficient. Simply put, it affects the sharpness of the temperature change. If you want fast heating, then set a high value (6-10), but then the room may overheat. If you want smooth heating, set 1-5. Choose the optimal value for yourself.
5. Check to see if it works correctly at different set temperatures over several days.
Read more about the algorithm [here](https://wdn.su/blog/1154).
@@ -185,6 +194,18 @@ In Google you can find instructions for tuning the PID controller.
### Use Equitherm mode + PID mode
@todo
## Dependencies
- [ESP8266Scheduler](https://github.com/nrwiersma/ESP8266Scheduler)
- [NTPClient](https://github.com/arduino-libraries/NTPClient)
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
- [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library)
- [PubSubClient](https://github.com/knolleary/pubsubclient)
- [TelnetStream](https://github.com/jandrassy/TelnetStream)
- [EEManager](https://github.com/GyverLibs/EEManager)
- [GyverPID](https://github.com/GyverLibs/GyverPID)
- [DallasTemperature](https://github.com/milesburton/Arduino-Temperature-Control-Library)
- [WiFiManager](https://github.com/tzapu/WiFiManager)
## Debug
To display DEBUG messages you must enable debug in settings (switch is disabled by default).
You can connect via Telnet to read messages. IP: esp8266 ip, port: 23
You can connect via Telnet to read messages. IP: ESP8266 ip, port: 23

1
assets/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/*.priv.*

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 689 KiB

After

Width:  |  Height:  |  Size: 716 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 236 KiB

14
build.py Normal file
View File

@@ -0,0 +1,14 @@
import shutil
import os
Import("env")
def post_build(source, target, env):
src = target[0].get_abspath()
dest = os.path.join(env["PROJECT_DIR"], "build", "firmware_%s_%s.bin" % (env.GetProjectOption("board"), env.GetProjectOption("version")))
#print("dest:"+dest)
#print("source:"+src)
shutil.copy(src, dest)
env.AddPostAction("$BUILD_DIR/firmware.bin", post_build)

View File

@@ -92,7 +92,7 @@ public:
const byte valueLB = response & 0xFF;
const byte valueHB = (response >> 8) & 0xFF;
float value = (int8_t) valueHB;
float value = (int8_t)valueHB;
return value + (float)valueLB / 256.0;
}

View File

@@ -0,0 +1,38 @@
class IntParameter : public WiFiManagerParameter {
public:
IntParameter(const char* id, const char* label, int value, const uint8_t length = 10) : WiFiManagerParameter("") {
init(id, label, String(value).c_str(), length, "", WFM_LABEL_DEFAULT);
}
int getValue() {
return atoi(WiFiManagerParameter::getValue());
}
};
class CheckboxParameter : public WiFiManagerParameter {
public:
const char* checked = "type=\"checkbox\" checked";
const char* noChecked = "type=\"checkbox\"";
const char* trueVal = "T";
CheckboxParameter(const char* id, const char* label, bool value) : WiFiManagerParameter("") {
init(id, label, value ? trueVal : "0", 1, "", WFM_LABEL_AFTER);
}
const char* getValue() const override {
return trueVal;
}
const char* getCustomHTML() const override {
return strcmp(WiFiManagerParameter::getValue(), trueVal) == 0 ? checked : noChecked;
}
bool getCheckboxValue() {
return strcmp(WiFiManagerParameter::getValue(), trueVal) == 0 ? true : false;
}
};
class SeparatorParameter : public WiFiManagerParameter {
public:
SeparatorParameter() : WiFiManagerParameter("<hr>") {}
};

4
otgateway.ino Normal file
View File

@@ -0,0 +1,4 @@
/*
This file is needed by the Arduino IDE because the ino file needs to be named as the directory name.
Don't worry, the Arduino compiler will "merge" all files, including src/main.cpp
*/

View File

@@ -8,9 +8,8 @@
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:d1_mini_pro]
[env]
platform = espressif8266
board = d1_mini_pro
framework = arduino
lib_deps =
nrwiersma/ESP8266Scheduler@^1.0
@@ -21,8 +20,20 @@ lib_deps =
jandrassy/TelnetStream@^1.2.4
gyverlibs/EEManager@^2.0
gyverlibs/GyverPID@^3.3
gyverlibs/microDS18B20@^3.10
https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2
milesburton/DallasTemperature@^3.11.0
https://github.com/Laxilef/WiFiManager/archive/refs/heads/patch-1.zip
; https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2
build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
upload_speed = 921600
monitor_speed = 115200
extra_scripts = post:build.py
version = 1.3.2
[env:d1_mini]
board = d1_mini
[env:d1_mini_lite]
board = d1_mini_lite
[env:d1_mini_pro]
board = d1_mini_pro

View File

@@ -27,10 +27,10 @@ public:
_deviceConfigUrl = value;
}
bool publishSelectOutdoorTempSource(bool enabledByDefault = true) {
bool publishSelectOutdoorSensorType(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"outdoorTempSource\": {% if value == 'Boiler' %}0{% elif value == 'Manual' %}1{% elif value == 'External' %}2{% endif %}}");
doc[F("command_template")] = F("{\"sensors\": {\"outdoor\": {\"type\": {% if value == 'Boiler' %}0{% elif value == 'Manual' %}1{% elif value == 'External' %}2{% endif %}}}}");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
@@ -40,24 +40,117 @@ public:
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_outdoorTempSource");
doc[F("object_id")] = _prefix + F("_outdoorTempSource");
doc[F("unique_id")] = _prefix + F("_outdoor_sensor_type");
doc[F("object_id")] = _prefix + F("_outdoor_sensor_type");
doc[F("entity_category")] = F("config");
doc[F("name")] = F("Outdoor temperature source");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{% if value_json.outdoorTempSource == 0 %}Boiler{% elif value_json.outdoorTempSource == 1 %}Manual{% elif value_json.outdoorTempSource == 2 %}External{% endif %}");
doc[F("value_template")] = F("{% if value_json.sensors.outdoor.type == 0 %}Boiler{% elif value_json.sensors.outdoor.type == 1 %}Manual{% elif value_json.sensors.outdoor.type == 2 %}External{% endif %}");
doc[F("options")][0] = F("Boiler");
doc[F("options")][1] = F("Manual");
doc[F("options")][2] = F("External");
client.beginPublish((F("homeassistant/select/") + _prefix + F("/outdoorTempSource/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
client.beginPublish((F("homeassistant/select/") + _prefix + F("/outdoor_sensor_type/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishSelectIndoorSensorType(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"sensors\": {\"indoor\": {\"type\": {% if value == 'Manual' %}1{% elif value == 'External' %}2{% endif %}}}}");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_indoor_sensor_type");
doc[F("object_id")] = _prefix + F("_indoor_sensor_type");
doc[F("entity_category")] = F("config");
doc[F("name")] = F("Indoor temperature source");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{% if value_json.sensors.indoor.type == 1 %}Manual{% elif value_json.sensors.indoor.type == 2 %}External{% endif %}");
doc[F("options")][0] = F("Manual");
doc[F("options")][1] = F("External");
client.beginPublish((F("homeassistant/select/") + _prefix + F("/indoor_sensor_type/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberOutdoorSensorOffset(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/settings");
doc[F("availability")][F("value_template")] = F("{{ iif(value_json.sensors.outdoor.type != 1, 'online', 'offline') }}");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_outdoor_sensor_offset");
doc[F("object_id")] = _prefix + F("_outdoor_sensor_offset");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Outdoor sensor offset");
doc[F("icon")] = F("mdi:altimeter");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.sensors.outdoor.offset|float(0)|round(2) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"sensors\": {\"outdoor\" : {\"offset\" : {{ value }}}}}");
doc[F("min")] = -10;
doc[F("max")] = 10;
doc[F("step")] = 0.1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/outdoor_sensor_offset/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberIndoorSensorOffset(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/settings");
doc[F("availability")][F("value_template")] = F("{{ iif(value_json.sensors.indoor.type != 1, 'online', 'offline') }}");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_indoor_sensor_offset");
doc[F("object_id")] = _prefix + F("_indoor_sensor_offset");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Indoor sensor offset");
doc[F("icon")] = F("mdi:altimeter");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.sensors.indoor.offset|float(0)|round(2) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"sensors\": {\"indoor\" : {\"offset\" : {{ value }}}}}");
doc[F("min")] = -10;
doc[F("max")] = 10;
doc[F("step")] = 0.1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/indoor_sensor_offset/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishSwitchDebug(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
@@ -83,9 +176,6 @@ public:
doc[F("payload_off")] = F("{\"debug\": false}");
client.beginPublish((F("homeassistant/switch/") + _prefix + F("/debug/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -116,9 +206,6 @@ public:
doc[F("payload_off")] = F("{\"emergency\": {\"enable\" : false}}");
client.beginPublish((F("homeassistant/switch/") + _prefix + F("/emergency/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -148,11 +235,9 @@ public:
doc[F("min")] = 5;
doc[F("max")] = 50;
doc[F("step")] = 0.5;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/emergency_target/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -160,7 +245,7 @@ public:
bool publishSwitchEmergencyUseEquitherm(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/settings");
doc[F("availability")][F("value_template")] = F("{{ iif(value_json.outdoorTempSource != 1, 'online', 'offline') }}");
doc[F("availability")][F("value_template")] = F("{{ iif(value_json.sensors.outdoor.type != 1, 'online', 'offline') }}");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
@@ -184,9 +269,6 @@ public:
doc[F("payload_off")] = F("{\"emergency\": {\"useEquitherm\" : false}}");
client.beginPublish((F("homeassistant/switch/") + _prefix + F("/emergency_use_equitherm/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -218,9 +300,6 @@ public:
doc[F("payload_off")] = F("{\"heating\": {\"enable\" : false}}");
client.beginPublish((F("homeassistant/switch/") + _prefix + F("/heating/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -251,9 +330,6 @@ public:
doc[F("payload_off")] = F("{\"heating\": {\"turbo\" : false}}");
client.beginPublish((F("homeassistant/switch/") + _prefix + F("/heating_turbo/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -284,11 +360,9 @@ public:
doc[F("min")] = minTemp;
doc[F("max")] = maxTemp;
doc[F("step")] = 0.5;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/heating_target/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -318,11 +392,9 @@ public:
doc[F("min")] = 0;
doc[F("max")] = 5;
doc[F("step")] = 0.1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/heating_hysteresis/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -351,9 +423,126 @@ public:
doc[F("value_template")] = F("{{ value_json.parameters.heatingSetpoint|int(0) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/heating_setpoint/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
bool publishSensorCurrentHeatingMinTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_current_heating_min_temp");
doc[F("object_id")] = _prefix + F("_current_heating_min_temp");
doc[F("entity_category")] = F("diagnostic");
doc[F("device_class")] = F("temperature");
doc[F("state_class")] = F("measurement");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Current heating min temp");
doc[F("icon")] = F("mdi:thermometer-chevron-down");
doc[F("state_topic")] = _prefix + F("/state");
doc[F("value_template")] = F("{{ value_json.parameters.heatingMinTemp|int(0) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/current_heating_min_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishSensorCurrentHeatingMaxTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_current_heating_max_temp");
doc[F("object_id")] = _prefix + F("_current_heating_max_temp");
doc[F("entity_category")] = F("diagnostic");
doc[F("device_class")] = F("temperature");
doc[F("state_class")] = F("measurement");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Current heating max temp");
doc[F("icon")] = F("mdi:thermometer-chevron-up");
doc[F("state_topic")] = _prefix + F("/state");
doc[F("value_template")] = F("{{ value_json.parameters.heatingMaxTemp|int(0) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/current_heating_max_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberHeatingMinTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_heating_min_temp");
doc[F("object_id")] = _prefix + F("_heating_min_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Heating min temp");
doc[F("icon")] = F("mdi:thermometer-chevron-down");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.heating.minTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"heating\": {\"minTemp\" : {{ value }}}}");
doc[F("min")] = 0;
doc[F("max")] = 99;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/heating_min_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberHeatingMaxTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_heating_max_temp");
doc[F("object_id")] = _prefix + F("_heating_max_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Heating max temp");
doc[F("icon")] = F("mdi:thermometer-chevron-up");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.heating.maxTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"heating\": {\"maxTemp\" : {{ value }}}}");
doc[F("min")] = 1;
doc[F("max")] = 100;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/heating_max_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
@@ -385,9 +574,6 @@ public:
doc[F("payload_off")] = F("{\"dhw\": {\"enable\" : false}}");
client.beginPublish((F("homeassistant/switch/") + _prefix + F("/dhw/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -416,13 +602,131 @@ public:
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"dhw\": {\"target\" : {{ value|int(0) }}}}");
doc[F("min")] = minTemp;
doc[F("max")] = maxTemp;
doc[F("max")] = maxTemp <= minTemp ? maxTemp : maxTemp;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/dhw_target/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
bool publishSensorCurrentDHWMinTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_current_dhw_min_temp");
doc[F("object_id")] = _prefix + F("_current_dhw_min_temp");
doc[F("entity_category")] = F("diagnostic");
doc[F("device_class")] = F("temperature");
doc[F("state_class")] = F("measurement");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Current DHW min temp");
doc[F("icon")] = F("mdi:thermometer-chevron-down");
doc[F("state_topic")] = _prefix + F("/state");
doc[F("value_template")] = F("{{ value_json.parameters.dhwMinTemp|int(0) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/current_dhw_min_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishSensorCurrentDHWMaxTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_current_dhw_max_temp");
doc[F("object_id")] = _prefix + F("_current_dhw_max_temp");
doc[F("entity_category")] = F("diagnostic");
doc[F("device_class")] = F("temperature");
doc[F("state_class")] = F("measurement");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Current DHW max temp");
doc[F("icon")] = F("mdi:thermometer-chevron-up");
doc[F("state_topic")] = _prefix + F("/state");
doc[F("value_template")] = F("{{ value_json.parameters.dhwMaxTemp|int(0) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/current_dhw_max_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberDHWMinTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_dhw_min_temp");
doc[F("object_id")] = _prefix + F("_dhw_min_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("DHW min temp");
doc[F("icon")] = F("mdi:thermometer-chevron-down");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.dhw.minTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"dhw\": {\"minTemp\" : {{ value }}}}");
doc[F("min")] = 0;
doc[F("max")] = 99;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/dhw_min_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberDHWMaxTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_dhw_max_temp");
doc[F("object_id")] = _prefix + F("_dhw_max_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("DHW max temp");
doc[F("icon")] = F("mdi:thermometer-chevron-up");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.dhw.maxTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"dhw\": {\"maxTemp\" : {{ value }}}}");
doc[F("min")] = 1;
doc[F("max")] = 100;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/dhw_max_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
@@ -453,9 +757,6 @@ public:
doc[F("payload_off")] = F("{\"pid\": {\"enable\" : false}}");
client.beginPublish((F("homeassistant/switch/") + _prefix + F("/pid/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -482,11 +783,9 @@ public:
doc[F("min")] = 0.001;
doc[F("max")] = 10;
doc[F("step")] = 0.001;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_p_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -513,11 +812,9 @@ public:
doc[F("min")] = 0;
doc[F("max")] = 10;
doc[F("step")] = 0.001;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_i_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -544,11 +841,73 @@ public:
doc[F("min")] = 0;
doc[F("max")] = 10;
doc[F("step")] = 0.001;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_d_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberPIDMinTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_pid_min_temp");
doc[F("object_id")] = _prefix + F("_pid_min_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("PID min temp");
doc[F("icon")] = F("mdi:thermometer-chevron-down");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.pid.minTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"pid\": {\"minTemp\" : {{ value }}}}");
doc[F("min")] = 0;
doc[F("max")] = 99;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_min_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberPIDMaxTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_pid_max_temp");
doc[F("object_id")] = _prefix + F("_pid_max_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("PID max temp");
doc[F("icon")] = F("mdi:thermometer-chevron-up");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.pid.maxTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"pid\": {\"maxTemp\" : {{ value }}}}");
doc[F("min")] = 1;
doc[F("max")] = 100;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_max_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
@@ -579,9 +938,6 @@ public:
doc[F("payload_off")] = F("{\"equitherm\": {\"enable\" : false}}");
client.beginPublish((F("homeassistant/switch/") + _prefix + F("/equitherm/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -608,11 +964,9 @@ public:
doc[F("min")] = 0.001;
doc[F("max")] = 5;
doc[F("step")] = 0.001;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/equitherm_n_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -639,11 +993,9 @@ public:
doc[F("min")] = 0;
doc[F("max")] = 10;
doc[F("step")] = 0.01;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/equitherm_k_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -672,11 +1024,9 @@ public:
doc[F("min")] = 0;
doc[F("max")] = 10;
doc[F("step")] = 0.01;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/equitherm_t_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -707,9 +1057,6 @@ public:
doc[F("payload_off")] = F("{\"tuning\": {\"enable\" : false}}");
client.beginPublish((F("homeassistant/switch/") + _prefix + F("/tuning/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -739,9 +1086,6 @@ public:
doc[F("options")][1] = F("PID");
client.beginPublish((F("homeassistant/select/") + _prefix + F("/tuning_regulator/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -769,9 +1113,6 @@ public:
doc[F("expire_after")] = 60;
client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/status/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -797,9 +1138,6 @@ public:
doc[F("value_template")] = F("{{ iif(value_json.states.otStatus, 'OFF', 'ON') }}");
client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/ot_status/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -826,9 +1164,6 @@ public:
doc[F("value_template")] = F("{{ iif(value_json.states.heating, 'ON', 'OFF') }}");
client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/heating/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -855,9 +1190,6 @@ public:
doc[F("value_template")] = F("{{ iif(value_json.states.dhw, 'ON', 'OFF') }}");
client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/dhw/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -884,9 +1216,6 @@ public:
doc[F("value_template")] = F("{{ iif(value_json.states.flame, 'ON', 'OFF') }}");
client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/flame/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -914,9 +1243,6 @@ public:
doc[F("value_template")] = F("{{ iif(value_json.states.fault, 'ON', 'OFF') }}");
client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/fault/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -943,9 +1269,6 @@ public:
doc[F("value_template")] = F("{{ iif(value_json.states.diagnostic, 'ON', 'OFF') }}");
client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/diagnostic/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -972,14 +1295,11 @@ public:
doc[F("value_template")] = F("{{ \"E%02d\"|format(value_json.states.faultCode) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/fault_code/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
bool publishSensorRssi(bool enabledByDefault = false) {
bool publishSensorRssi(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc["device"]["identifiers"][0] = _prefix;
doc["device"]["sw_version"] = _deviceVersion;
@@ -1003,9 +1323,6 @@ public:
doc["value_template"] = "{{ value_json.states.rssi|float(0)|round(1) }}";
client.beginPublish((F("homeassistant/sensor/") + _prefix + "/rssi/config").c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -1035,9 +1352,6 @@ public:
doc[F("value_template")] = F("{{ value_json.sensors.modulation|float(0)|round(0) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/modulation/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -1066,9 +1380,6 @@ public:
doc[F("value_template")] = F("{{ value_json.sensors.pressure|float(0)|round(2) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/pressure/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -1076,7 +1387,6 @@ public:
bool publishNumberIndoorTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
//doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
@@ -1097,13 +1407,41 @@ public:
doc[F("value_template")] = F("{{ value_json.temperatures.indoor|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/state/set");
doc[F("command_template")] = F("{\"temperatures\": {\"indoor\":{{ value }}}}");
doc[F("min")] = -70;
doc[F("max")] = 50;
doc[F("min")] = -99;
doc[F("max")] = 99;
doc[F("step")] = 0.01;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/indoor_temp/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
bool publishSensorIndoorTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][0][F("topic")] = _prefix + F("/status");
doc[F("availability_mode")] = F("any");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_indoor_temp");
doc[F("object_id")] = _prefix + F("_indoor_temp");
doc[F("entity_category")] = F("diagnostic");
doc[F("device_class")] = F("temperature");
doc[F("state_class")] = F("measurement");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Indoor temperature");
doc[F("icon")] = F("mdi:home-thermometer");
doc[F("state_topic")] = _prefix + F("/state");
doc[F("value_template")] = F("{{ value_json.temperatures.indoor|float(0)|round(1) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/indoor_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
@@ -1130,13 +1468,12 @@ public:
doc[F("value_template")] = F("{{ value_json.temperatures.outdoor|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/state/set");
doc[F("command_template")] = F("{\"temperatures\": {\"outdoor\":{{ value }}}}");
doc[F("min")] = -70;
doc[F("max")] = 50;
doc[F("min")] = -99;
doc[F("max")] = 99;
doc[F("step")] = 0.01;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/outdoor_temp/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -1144,8 +1481,6 @@ public:
bool publishSensorOutdoorTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][0][F("topic")] = _prefix + F("/status");
doc[F("availability")][1][F("topic")] = _prefix + F("/settings");
doc[F("availability")][1][F("value_template")] = F("{{ iif(value_json.outdoorTempSource == 2, 'online', 'offline') }}");
doc[F("availability_mode")] = F("any");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
@@ -1168,9 +1503,6 @@ public:
doc[F("value_template")] = F("{{ value_json.temperatures.outdoor|float(0)|round(1) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/outdoor_temp/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -1199,9 +1531,6 @@ public:
doc[F("value_template")] = F("{{ value_json.temperatures.heating|float(0)|round(2) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/heating_temp/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -1230,16 +1559,13 @@ public:
doc[F("value_template")] = F("{{ value_json.temperatures.dhw|float(0)|round(2) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/dhw_temp/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
bool publishClimateHeating(byte minTemp = 20, byte maxTemp = 90, bool enabledByDefault = true) {
StaticJsonDocument<2048> doc;
StaticJsonDocument<2560> doc;
doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
@@ -1288,15 +1614,12 @@ public:
doc[F("temp_step")] = 0.5;
client.beginPublish((F("homeassistant/climate/") + _prefix + F("_heating/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
bool publishClimateDHW(byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
StaticJsonDocument<2560> doc;
doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
@@ -1337,9 +1660,6 @@ public:
doc[F("max_temp")] = maxTemp;
client.beginPublish((F("homeassistant/climate/") + _prefix + F("_dhw/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
@@ -1353,6 +1673,50 @@ public:
return client.publish((F("homeassistant/sensor/") + _prefix + F("/outdoor_temp/config")).c_str(), NULL, true);
}
bool deleteNumberIndoorTemp() {
return client.publish((F("homeassistant/number/") + _prefix + F("/indoor_temp/config")).c_str(), NULL, true);
}
bool deleteSensorIndoorTemp() {
return client.publish((F("homeassistant/sensor/") + _prefix + F("/indoor_temp/config")).c_str(), NULL, true);
}
bool deleteSwitchDHW() {
return client.publish((F("homeassistant/switch/") + _prefix + F("/dhw/config")).c_str(), NULL, true);
}
bool deleteSensorCurrentDHWMinTemp() {
return client.publish((F("homeassistant/sensor/") + _prefix + F("/current_dhw_min_temp/config")).c_str(), NULL, true);
}
bool deleteSensorCurrentDHWMaxTemp() {
return client.publish((F("homeassistant/sensor/") + _prefix + F("/current_dhw_max_temp/config")).c_str(), NULL, true);
}
bool deleteNumberDHWMinTemp() {
return client.publish((F("homeassistant/number/") + _prefix + F("/dhw_min_temp/config")).c_str(), NULL, true);
}
bool deleteNumberDHWMaxTemp() {
return client.publish((F("homeassistant/number/") + _prefix + F("/dhw_max_temp/config")).c_str(), NULL, true);
}
bool deleteBinSensorDHW() {
return client.publish((F("homeassistant/binary_sensor/") + _prefix + F("/dhw/config")).c_str(), NULL, true);
}
bool deleteSensorDHWTemp() {
return client.publish((F("homeassistant/sensor/") + _prefix + F("/dhw_temp/config")).c_str(), NULL, true);
}
bool deleteNumberDHWTarget() {
return client.publish((F("homeassistant/number/") + _prefix + F("/dhw_target/config")).c_str(), NULL, true);
}
bool deleteClimateDHW() {
return client.publish((F("homeassistant/climate/") + _prefix + F("_dhw/config")).c_str(), NULL, true);
}
private:
String _prefix = "opentherm";
String _deviceVersion = "1.0";

View File

@@ -2,9 +2,9 @@ extern MqttTask* tMqtt;
extern SensorsTask* tSensors;
extern OpenThermTask* tOt;
class MainTask: public Task {
class MainTask : public Task {
public:
MainTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {}
MainTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
protected:
unsigned long lastHeapInfo = 0;
@@ -13,7 +13,6 @@ protected:
void setup() {
pinMode(LED_STATUS_PIN, OUTPUT);
//pinMode(LED_OT_RX_PIN, OUTPUT);
}
void loop() {
@@ -26,7 +25,7 @@ protected:
tMqtt->enable();
}
if ( firstFailConnect != 0 ) {
if (firstFailConnect != 0) {
firstFailConnect = 0;
}
@@ -41,7 +40,7 @@ protected:
if (firstFailConnect == 0) {
firstFailConnect = millis();
}
if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) {
vars.states.emergency = true;
INFO("Emergency mode enabled");
@@ -49,12 +48,6 @@ protected:
}
}
if (!tSensors->isEnabled() && settings.outdoorTempSource == 2) {
tSensors->enable();
} else if (tSensors->isEnabled() && settings.outdoorTempSource != 2) {
tSensors->disable();
}
if (!tOt->isEnabled() && settings.opentherm.inPin > 0 && settings.opentherm.outPin > 0 && settings.opentherm.inPin != settings.opentherm.outPin) {
tOt->enable();
}

View File

@@ -8,9 +8,9 @@ PubSubClient client(espClient);
HomeAssistantHelper haHelper;
class MqttTask: public Task {
class MqttTask : public Task {
public:
MqttTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {}
MqttTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
protected:
unsigned long lastReconnectAttempt = 0;
@@ -19,7 +19,6 @@ protected:
void setup() {
DEBUG("[MQTT] Started");
client.setServer(settings.mqtt.server, settings.mqtt.port);
client.setCallback(__callback);
haHelper.setPrefix(settings.mqtt.prefix);
haHelper.setDeviceVersion(OT_GATEWAY_VERSION);
@@ -32,6 +31,7 @@ protected:
if (!client.connected() && millis() - lastReconnectAttempt >= MQTT_RECONNECT_INTERVAL) {
INFO_F("Mqtt not connected, state: %i, connecting to server %s...\n", client.state(), settings.mqtt.server);
client.setServer(settings.mqtt.server, settings.mqtt.port);
if (client.connect(settings.hostname, settings.mqtt.user, settings.mqtt.password)) {
INFO("Connected to MQTT server");
@@ -50,7 +50,7 @@ protected:
if (firstFailConnect == 0) {
firstFailConnect = millis();
}
if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) {
vars.states.emergency = true;
INFO("Emergency mode enabled");
@@ -93,15 +93,6 @@ protected:
flag = true;
}
if (!doc["outdoorTempSource"].isNull() && doc["outdoorTempSource"].is<int>() && doc["outdoorTempSource"] >= 0 && doc["outdoorTempSource"] <= 2) {
settings.outdoorTempSource = doc["outdoorTempSource"];
flag = true;
}
if (!doc["mqtt"]["interval"].isNull() && doc["mqtt"]["interval"].is<int>() && doc["mqtt"]["interval"] >= 1000 && doc["mqtt"]["interval"] <= 120000) {
settings.mqtt.interval = doc["mqtt"]["interval"].as<unsigned int>();
flag = true;
}
// emergency
if (!doc["emergency"]["enable"].isNull() && doc["emergency"]["enable"].is<bool>()) {
@@ -109,9 +100,11 @@ protected:
flag = true;
}
if (!doc["emergency"]["target"].isNull() && (doc["emergency"]["target"].is<float>() || doc["emergency"]["target"].is<int>())) {
settings.emergency.target = round(doc["emergency"]["target"].as<float>() * 10) / 10;
flag = true;
if (!doc["emergency"]["target"].isNull() && doc["emergency"]["target"].is<float>()) {
if (doc["emergency"]["target"].as<float>() > 0 && doc["emergency"]["target"].as<float>() < 100) {
settings.emergency.target = round(doc["emergency"]["target"].as<float>() * 10) / 10;
flag = true;
}
}
if (!doc["emergency"]["useEquitherm"].isNull() && doc["emergency"]["useEquitherm"].is<bool>()) {
@@ -119,6 +112,7 @@ protected:
flag = true;
}
// heating
if (!doc["heating"]["enable"].isNull() && doc["heating"]["enable"].is<bool>()) {
settings.heating.enable = doc["heating"]["enable"].as<bool>();
@@ -130,46 +124,106 @@ protected:
flag = true;
}
if (!doc["heating"]["target"].isNull() && (doc["heating"]["target"].is<float>() || doc["heating"]["target"].is<int>())) {
settings.heating.target = round(doc["heating"]["target"].as<float>() * 10) / 10;
flag = true;
if (!doc["heating"]["target"].isNull() && doc["heating"]["target"].is<float>()) {
if (doc["heating"]["target"].as<float>() > 0 && doc["heating"]["target"].as<float>() < 100) {
settings.heating.target = round(doc["heating"]["target"].as<float>() * 10) / 10;
flag = true;
}
}
if (!doc["heating"]["hysteresis"].isNull() && (doc["heating"]["hysteresis"].is<float>() || doc["heating"]["hysteresis"].is<int>())) {
settings.heating.hysteresis = round(doc["heating"]["hysteresis"].as<float>() * 10) / 10;
flag = true;
if (!doc["heating"]["hysteresis"].isNull() && doc["heating"]["hysteresis"].is<float>()) {
if (doc["heating"]["hysteresis"].as<float>() >= 0 && doc["heating"]["hysteresis"].as<float>() <= 5) {
settings.heating.hysteresis = round(doc["heating"]["hysteresis"].as<float>() * 10) / 10;
flag = true;
}
}
if (!doc["heating"]["maxTemp"].isNull() && doc["heating"]["maxTemp"].is<unsigned char>()) {
if (doc["heating"]["maxTemp"].as<unsigned char>() > 0 && doc["heating"]["maxTemp"].as<unsigned char>() <= 100 && doc["heating"]["maxTemp"].as<unsigned char>() > settings.heating.minTemp) {
settings.heating.maxTemp = doc["heating"]["maxTemp"].as<unsigned char>();
vars.parameters.heatingMaxTemp = settings.heating.maxTemp;
flag = true;
}
}
if (!doc["heating"]["minTemp"].isNull() && doc["heating"]["minTemp"].is<unsigned char>()) {
if (doc["heating"]["minTemp"].as<unsigned char>() >= 0 && doc["heating"]["minTemp"].as<unsigned char>() < 100 && doc["heating"]["minTemp"].as<unsigned char>() < settings.heating.maxTemp) {
settings.heating.minTemp = doc["heating"]["minTemp"].as<unsigned char>();
vars.parameters.heatingMinTemp = settings.heating.minTemp;
flag = true;
}
}
// dhw
if (!doc["dhw"]["enable"].isNull() && doc["dhw"]["enable"].is<bool>()) {
settings.dhw.enable = doc["dhw"]["enable"].as<bool>();
flag = true;
}
if (!doc["dhw"]["target"].isNull() && doc["dhw"]["target"].is<int>()) {
settings.dhw.target = doc["dhw"]["target"].as<int>();
flag = true;
if (!doc["dhw"]["target"].isNull() && doc["dhw"]["target"].is<unsigned char>()) {
if (doc["dhw"]["target"].as<unsigned char>() >= 0 && doc["dhw"]["target"].as<unsigned char>() < 100) {
settings.dhw.target = doc["dhw"]["target"].as<unsigned char>();
flag = true;
}
}
if (!doc["dhw"]["maxTemp"].isNull() && doc["dhw"]["maxTemp"].is<unsigned char>()) {
if (doc["dhw"]["maxTemp"].as<unsigned char>() > 0 && doc["dhw"]["maxTemp"].as<unsigned char>() <= 100 && doc["dhw"]["maxTemp"].as<unsigned char>() > settings.dhw.minTemp) {
settings.dhw.maxTemp = doc["dhw"]["maxTemp"].as<unsigned char>();
vars.parameters.dhwMaxTemp = settings.dhw.maxTemp;
flag = true;
}
}
if (!doc["dhw"]["minTemp"].isNull() && doc["dhw"]["minTemp"].is<unsigned char>()) {
if (doc["dhw"]["minTemp"].as<unsigned char>() >= 0 && doc["dhw"]["minTemp"].as<unsigned char>() < 100 && doc["dhw"]["minTemp"].as<unsigned char>() < settings.dhw.maxTemp) {
settings.dhw.minTemp = doc["dhw"]["minTemp"].as<unsigned char>();
vars.parameters.dhwMinTemp = settings.dhw.minTemp;
flag = true;
}
}
// pid
if (!doc["pid"]["enable"].isNull() && doc["pid"]["enable"].is<bool>()) {
settings.pid.enable = doc["pid"]["enable"].as<bool>();
flag = true;
}
if (!doc["pid"]["p_factor"].isNull() && (doc["pid"]["p_factor"].is<float>() || doc["pid"]["p_factor"].is<int>())) {
settings.pid.p_factor = round(doc["pid"]["p_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["pid"]["p_factor"].isNull() && doc["pid"]["p_factor"].is<float>()) {
if (doc["pid"]["p_factor"].as<float>() >= 0 && doc["pid"]["p_factor"].as<float>() <= 20) {
settings.pid.p_factor = round(doc["pid"]["p_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["pid"]["i_factor"].isNull() && (doc["pid"]["i_factor"].is<float>() || doc["pid"]["i_factor"].is<int>())) {
settings.pid.i_factor = round(doc["pid"]["i_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["pid"]["i_factor"].isNull() && doc["pid"]["i_factor"].is<float>()) {
if (doc["pid"]["i_factor"].as<float>() >= 0 && doc["pid"]["i_factor"].as<float>() <= 20) {
settings.pid.i_factor = round(doc["pid"]["i_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["pid"]["d_factor"].isNull() && (doc["pid"]["d_factor"].is<float>() || doc["pid"]["d_factor"].is<int>())) {
settings.pid.d_factor = round(doc["pid"]["d_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["pid"]["d_factor"].isNull() && doc["pid"]["d_factor"].is<float>()) {
if (doc["pid"]["d_factor"].as<float>() >= 0 && doc["pid"]["d_factor"].as<float>() <= 20) {
settings.pid.d_factor = round(doc["pid"]["d_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["pid"]["maxTemp"].isNull() && doc["pid"]["maxTemp"].is<unsigned char>()) {
if (doc["pid"]["maxTemp"].as<unsigned char>() > 0 && doc["pid"]["maxTemp"].as<unsigned char>() <= 100 && doc["pid"]["maxTemp"].as<unsigned char>() > settings.pid.minTemp) {
settings.pid.maxTemp = doc["pid"]["maxTemp"].as<unsigned char>();
flag = true;
}
}
if (!doc["pid"]["minTemp"].isNull() && doc["pid"]["minTemp"].is<unsigned char>()) {
if (doc["pid"]["minTemp"].as<unsigned char>() >= 0 && doc["pid"]["minTemp"].as<unsigned char>() < 100 && doc["pid"]["minTemp"].as<unsigned char>() < settings.pid.maxTemp) {
settings.pid.minTemp = doc["pid"]["minTemp"].as<unsigned char>();
flag = true;
}
}
// equitherm
@@ -178,21 +232,58 @@ protected:
flag = true;
}
if (!doc["equitherm"]["n_factor"].isNull() && (doc["equitherm"]["n_factor"].is<float>() || doc["equitherm"]["n_factor"].is<int>())) {
settings.equitherm.n_factor = round(doc["equitherm"]["n_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["equitherm"]["n_factor"].isNull() && doc["equitherm"]["n_factor"].is<float>()) {
if (doc["equitherm"]["n_factor"].as<float>() >= 0 && doc["equitherm"]["n_factor"].as<float>() <= 20) {
settings.equitherm.n_factor = round(doc["equitherm"]["n_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["equitherm"]["k_factor"].isNull() && (doc["equitherm"]["k_factor"].is<float>() || doc["equitherm"]["k_factor"].is<int>())) {
settings.equitherm.k_factor = round(doc["equitherm"]["k_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["equitherm"]["k_factor"].isNull() && doc["equitherm"]["k_factor"].is<float>()) {
if (doc["equitherm"]["k_factor"].as<float>() >= 0 && doc["equitherm"]["k_factor"].as<float>() <= 20) {
settings.equitherm.k_factor = round(doc["equitherm"]["k_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["equitherm"]["t_factor"].isNull() && (doc["equitherm"]["t_factor"].is<float>() || doc["equitherm"]["t_factor"].is<int>())) {
settings.equitherm.t_factor = round(doc["equitherm"]["t_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["equitherm"]["t_factor"].isNull() && doc["equitherm"]["t_factor"].is<float>()) {
if (doc["equitherm"]["t_factor"].as<float>() >= 0 && doc["equitherm"]["t_factor"].as<float>() <= 20) {
settings.equitherm.t_factor = round(doc["equitherm"]["t_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
// sensors
if (!doc["sensors"]["outdoor"]["type"].isNull() && doc["sensors"]["outdoor"]["type"].is<unsigned char>()) {
if (doc["sensors"]["outdoor"]["type"].as<unsigned char>() >= 0 && doc["sensors"]["outdoor"]["type"].as<unsigned char>() <= 2) {
settings.sensors.outdoor.type = doc["sensors"]["outdoor"]["type"].as<unsigned char>();
flag = true;
}
}
if (!doc["sensors"]["outdoor"]["offset"].isNull() && doc["sensors"]["outdoor"]["offset"].is<float>()) {
if (doc["sensors"]["outdoor"]["offset"].as<float>() >= -10 && doc["sensors"]["outdoor"]["offset"].as<float>() <= 10) {
settings.sensors.outdoor.offset = round(doc["sensors"]["outdoor"]["offset"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["sensors"]["indoor"]["type"].isNull() && doc["sensors"]["indoor"]["type"].is<unsigned char>()) {
if (doc["sensors"]["indoor"]["type"].as<unsigned char>() >= 1 && doc["sensors"]["indoor"]["type"].as<unsigned char>() <= 2) {
settings.sensors.indoor.type = doc["sensors"]["indoor"]["type"].as<unsigned char>();
flag = true;
}
}
if (!doc["sensors"]["indoor"]["offset"].isNull() && doc["sensors"]["indoor"]["offset"].is<float>()) {
if (doc["sensors"]["indoor"]["offset"].as<float>() >= -10 && doc["sensors"]["indoor"]["offset"].as<float>() <= 10) {
settings.sensors.indoor.offset = round(doc["sensors"]["indoor"]["offset"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (flag) {
eeSettings.update();
publish(true);
@@ -215,27 +306,33 @@ protected:
flag = true;
}
if (!doc["tuning"]["regulator"].isNull() && doc["tuning"]["regulator"].is<int>() && doc["tuning"]["regulator"] >= 0 && doc["tuning"]["regulator"] <= 1) {
vars.tuning.regulator = doc["tuning"]["regulator"];
flag = true;
if (!doc["tuning"]["regulator"].isNull() && doc["tuning"]["regulator"].is<unsigned char>()) {
if (doc["tuning"]["regulator"].as<unsigned char>() >= 0 && doc["tuning"]["regulator"].as<unsigned char>() <= 1) {
vars.tuning.regulator = doc["tuning"]["regulator"].as<unsigned char>();
flag = true;
}
}
if (!doc["temperatures"]["indoor"].isNull() && (doc["temperatures"]["indoor"].is<float>() || doc["temperatures"]["indoor"].is<int>())) {
vars.temperatures.indoor = round(doc["temperatures"]["indoor"].as<float>() * 100) / 100;
flag = true;
if (!doc["temperatures"]["indoor"].isNull() && doc["temperatures"]["indoor"].is<float>()) {
if (settings.sensors.indoor.type == 1 && doc["temperatures"]["indoor"].as<float>() > -100 && doc["temperatures"]["indoor"].as<float>() < 100) {
vars.temperatures.indoor = round(doc["temperatures"]["indoor"].as<float>() * 100) / 100;
flag = true;
}
}
if (!doc["temperatures"]["outdoor"].isNull() && (doc["temperatures"]["outdoor"].is<float>() || doc["temperatures"]["outdoor"].is<int>()) && settings.outdoorTempSource == 1) {
vars.temperatures.outdoor = round(doc["temperatures"]["outdoor"].as<float>() * 100) / 100;
flag = true;
if (!doc["temperatures"]["outdoor"].isNull() && doc["temperatures"]["outdoor"].is<float>()) {
if (settings.sensors.outdoor.type == 1 && doc["temperatures"]["outdoor"].as<float>() > -100 && doc["temperatures"]["outdoor"].as<float>() < 100) {
vars.temperatures.outdoor = round(doc["temperatures"]["outdoor"].as<float>() * 100) / 100;
flag = true;
}
}
if (!doc["restart"].isNull() && doc["restart"].is<bool>() && doc["restart"]) {
if (!doc["restart"].isNull() && doc["restart"].is<bool>() && doc["restart"].as<bool>()) {
DEBUG("Received restart message...");
eeSettings.updateNow();
Scheduler.delay(10000);
DEBUG("Restart...");
eeSettings.updateNow();
ESP.restart();
}
@@ -275,7 +372,10 @@ protected:
static void publishHaEntities() {
// main
haHelper.publishSelectOutdoorTempSource();
haHelper.publishSelectOutdoorSensorType();
haHelper.publishSelectIndoorSensorType();
haHelper.publishNumberOutdoorSensorOffset(false);
haHelper.publishNumberIndoorSensorOffset(false);
haHelper.publishSwitchDebug(false);
// emergency
@@ -289,16 +389,18 @@ protected:
//haHelper.publishNumberHeatingTarget(false);
haHelper.publishNumberHeatingHysteresis();
haHelper.publishSensorHeatingSetpoint(false);
// dhw
haHelper.publishSwitchDHW(false);
//haHelper.publishNumberDHWTarget(false);
haHelper.publishSensorCurrentHeatingMinTemp(false);
haHelper.publishSensorCurrentHeatingMaxTemp(false);
haHelper.publishNumberHeatingMinTemp(false);
haHelper.publishNumberHeatingMaxTemp(false);
// pid
haHelper.publishSwitchPID();
haHelper.publishNumberPIDFactorP();
haHelper.publishNumberPIDFactorI();
haHelper.publishNumberPIDFactorD();
haHelper.publishNumberPIDMinTemp(false);
haHelper.publishNumberPIDMaxTemp(false);
// equitherm
haHelper.publishSwitchEquitherm();
@@ -314,12 +416,11 @@ protected:
haHelper.publishBinSensorStatus();
haHelper.publishBinSensorOtStatus();
haHelper.publishBinSensorHeating();
haHelper.publishBinSensorDHW();
haHelper.publishBinSensorFlame();
haHelper.publishBinSensorFault();
haHelper.publishBinSensorDiagnostic();
haHelper.publishSensorFaultCode();
haHelper.publishSensorRssi();
haHelper.publishSensorRssi(false);
// sensors
haHelper.publishSensorModulation(false);
@@ -329,22 +430,45 @@ protected:
haHelper.publishNumberIndoorTemp();
//haHelper.publishNumberOutdoorTemp();
haHelper.publishSensorHeatingTemp();
haHelper.publishSensorDHWTemp();
}
static bool publishNonStaticHaEntities(bool force = false) {
static byte _heatingMinTemp;
static byte _heatingMaxTemp;
static byte _dhwMinTemp;
static byte _dhwMaxTemp;
static bool _editableOutdoorTemp;
static byte _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp;
static bool _editableOutdoorTemp, _editableIndoorTemp, _dhwPresent;
bool published = false;
bool isStupidMode = !settings.pid.enable && !settings.equitherm.enable;
byte heatingMinTemp = isStupidMode ? vars.parameters.heatingMinTemp : 10;
byte heatingMaxTemp = isStupidMode ? vars.parameters.heatingMaxTemp : 30;
bool editableOutdoorTemp = settings.outdoorTempSource == 1;
bool editableOutdoorTemp = settings.sensors.outdoor.type == 1;
bool editableIndoorTemp = settings.sensors.indoor.type == 1;
if (force || _dhwPresent != settings.opentherm.dhwPresent) {
_dhwPresent = settings.opentherm.dhwPresent;
if (_dhwPresent) {
haHelper.publishSwitchDHW(false);
haHelper.publishSensorCurrentDHWMinTemp(false);
haHelper.publishSensorCurrentDHWMaxTemp(false);
haHelper.publishNumberDHWMinTemp(false);
haHelper.publishNumberDHWMaxTemp(false);
haHelper.publishBinSensorDHW();
haHelper.publishSensorDHWTemp();
} else {
haHelper.deleteSwitchDHW();
haHelper.deleteSensorCurrentDHWMinTemp();
haHelper.deleteSensorCurrentDHWMaxTemp();
haHelper.deleteNumberDHWMinTemp();
haHelper.deleteNumberDHWMaxTemp();
haHelper.deleteBinSensorDHW();
haHelper.deleteSensorDHWTemp();
haHelper.deleteNumberDHWTarget();
haHelper.deleteClimateDHW();
}
published = true;
}
if (force || _heatingMinTemp != heatingMinTemp || _heatingMaxTemp != heatingMaxTemp) {
if (settings.heating.target < heatingMinTemp || settings.heating.target > heatingMaxTemp) {
@@ -360,7 +484,7 @@ protected:
published = true;
}
if (force || _dhwMinTemp != vars.parameters.dhwMinTemp || _dhwMaxTemp != vars.parameters.dhwMaxTemp) {
if (_dhwPresent && (force || _dhwMinTemp != vars.parameters.dhwMinTemp || _dhwMaxTemp != vars.parameters.dhwMaxTemp)) {
_dhwMinTemp = vars.parameters.dhwMinTemp;
_dhwMaxTemp = vars.parameters.dhwMaxTemp;
@@ -384,6 +508,20 @@ protected:
published = true;
}
if (force || _editableIndoorTemp != editableIndoorTemp) {
_editableIndoorTemp = editableIndoorTemp;
if (editableIndoorTemp) {
haHelper.deleteSensorIndoorTemp();
haHelper.publishNumberIndoorTemp();
} else {
haHelper.deleteNumberIndoorTemp();
haHelper.publishSensorIndoorTemp();
}
published = true;
}
return published;
}
@@ -391,7 +529,6 @@ protected:
StaticJsonDocument<2048> doc;
doc["debug"] = settings.debug;
doc["outdoorTempSource"] = settings.outdoorTempSource;
doc["emergency"]["enable"] = settings.emergency.enable;
doc["emergency"]["target"] = settings.emergency.target;
@@ -401,20 +538,32 @@ protected:
doc["heating"]["turbo"] = settings.heating.turbo;
doc["heating"]["target"] = settings.heating.target;
doc["heating"]["hysteresis"] = settings.heating.hysteresis;
doc["heating"]["minTemp"] = settings.heating.minTemp;
doc["heating"]["maxTemp"] = settings.heating.maxTemp;
doc["dhw"]["enable"] = settings.dhw.enable;
doc["dhw"]["target"] = settings.dhw.target;
doc["dhw"]["minTemp"] = settings.dhw.minTemp;
doc["dhw"]["maxTemp"] = settings.dhw.maxTemp;
doc["pid"]["enable"] = settings.pid.enable;
doc["pid"]["p_factor"] = settings.pid.p_factor;
doc["pid"]["i_factor"] = settings.pid.i_factor;
doc["pid"]["d_factor"] = settings.pid.d_factor;
doc["pid"]["minTemp"] = settings.pid.minTemp;
doc["pid"]["maxTemp"] = settings.pid.maxTemp;
doc["equitherm"]["enable"] = settings.equitherm.enable;
doc["equitherm"]["n_factor"] = settings.equitherm.n_factor;
doc["equitherm"]["k_factor"] = settings.equitherm.k_factor;
doc["equitherm"]["t_factor"] = settings.equitherm.t_factor;
doc["sensors"]["outdoor"]["type"] = settings.sensors.outdoor.type;
doc["sensors"]["outdoor"]["offset"] = settings.sensors.outdoor.offset;
doc["sensors"]["indoor"]["type"] = settings.sensors.indoor.type;
doc["sensors"]["indoor"]["offset"] = settings.sensors.indoor.offset;
client.beginPublish(topic, measureJson(doc), false);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
@@ -446,6 +595,7 @@ protected:
doc["temperatures"]["heating"] = vars.temperatures.heating;
doc["temperatures"]["dhw"] = vars.temperatures.dhw;
doc["parameters"]["heatingEnabled"] = vars.parameters.heatingEnabled;
doc["parameters"]["heatingMinTemp"] = vars.parameters.heatingMinTemp;
doc["parameters"]["heatingMaxTemp"] = vars.parameters.heatingMaxTemp;
doc["parameters"]["heatingSetpoint"] = vars.parameters.heatingSetpoint;

View File

@@ -3,12 +3,17 @@
CustomOpenTherm* ot;
class OpenThermTask: public Task {
class OpenThermTask : public Task {
public:
OpenThermTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {}
OpenThermTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
protected:
void setup() {
vars.parameters.heatingMinTemp = settings.heating.minTemp;
vars.parameters.heatingMaxTemp = settings.heating.maxTemp;
vars.parameters.dhwMinTemp = settings.dhw.minTemp;
vars.parameters.dhwMaxTemp = settings.dhw.maxTemp;
ot = new CustomOpenTherm(settings.opentherm.inPin, settings.opentherm.outPin);
ot->begin(handleInterrupt, responseCallback);
@@ -23,7 +28,7 @@ protected:
static byte currentHeatingTemp, currentDHWTemp = 0;
unsigned long localResponse;
if ( setMasterMemberIdCode() ) {
if (setMasterMemberIdCode()) {
DEBUG_F("Slave member id code: %u\r\n", vars.parameters.slaveMemberIdCode);
DEBUG_F("Master member id code: %u\r\n", settings.opentherm.memberIdCode > 0 ? settings.opentherm.memberIdCode : vars.parameters.slaveMemberIdCode);
@@ -31,25 +36,30 @@ protected:
WARN("Slave member id failed");
}
bool heatingEnable = (vars.states.emergency || settings.heating.enable) && pump && isReady();
bool heatingEnabled = (vars.states.emergency || settings.heating.enable) && pump && isReady();
localResponse = ot->setBoilerStatus(
heatingEnable,
settings.dhw.enable,
heatingEnabled,
settings.opentherm.dhwPresent && settings.dhw.enable,
false, false, true, false, false
);
if (!ot->isValidResponse(localResponse)) {
WARN_F("Invalid response after setBoilerStatus: %s\r\n", ot->statusToString(ot->getLastResponseStatus()));
return;
}
INFO_F("Heating enabled: %d\r\n", heatingEnable);
setMaxModulationLevel(heatingEnable ? 100 : 0);
if (vars.parameters.heatingEnabled != heatingEnabled) {
vars.parameters.heatingEnabled = heatingEnabled;
INFO_F("Heating enabled: %s\r\n", heatingEnabled ? "on\0" : "off\0");
}
vars.states.heating = ot->isCentralHeatingActive(localResponse);
vars.states.dhw = ot->isHotWaterActive(localResponse);
vars.states.dhw = settings.opentherm.dhwPresent ? ot->isHotWaterActive(localResponse) : false;
vars.states.flame = ot->isFlameOn(localResponse);
vars.states.fault = ot->isFault(localResponse);
vars.states.diagnostic = ot->isDiagnostic(localResponse);
setMaxModulationLevel(heatingEnabled ? 100 : 0);
yield();
// Команды чтения данных котла
@@ -60,10 +70,12 @@ protected:
DEBUG_F("Master type: %u, version: %u\r\n", vars.parameters.masterType, vars.parameters.masterVersion);
DEBUG_F("Slave type: %u, version: %u\r\n", vars.parameters.slaveType, vars.parameters.slaveVersion);
updateMinMaxDhwTemp();
if (settings.opentherm.dhwPresent) {
updateMinMaxDhwTemp();
}
updateMinMaxHeatingTemp();
if (settings.outdoorTempSource == 0) {
if (settings.sensors.outdoor.type == 0) {
updateOutsideTemp();
}
if (vars.states.fault) {
@@ -71,7 +83,7 @@ protected:
ot->sendBoilerReset();
}
if ( vars.states.diagnostic ) {
if (vars.states.diagnostic) {
ot->sendServiceReset();
}
@@ -80,28 +92,27 @@ protected:
}
updatePressure();
if ( settings.dhw.enable || settings.heating.enable || heatingEnable ) {
if ((settings.opentherm.dhwPresent && settings.dhw.enable) || settings.heating.enable || heatingEnabled) {
updateModulationLevel();
}
if ( settings.dhw.enable ) {
} else {
vars.sensors.modulation = 0;
}
yield();
if (settings.opentherm.dhwPresent) {
updateDHWTemp();
} else {
vars.temperatures.dhw = 0;
}
if ( settings.heating.enable || heatingEnable ) {
updateHeatingTemp();
} else {
vars.temperatures.heating = 0;
}
updateHeatingTemp();
yield();
//
// Температура ГВС
byte newDHWTemp = settings.dhw.target;
if (settings.dhw.enable && newDHWTemp != currentDHWTemp) {
if (settings.opentherm.dhwPresent && settings.dhw.enable && (needSetDhwTemp() || newDHWTemp != currentDHWTemp)) {
if (newDHWTemp < vars.parameters.dhwMinTemp || newDHWTemp > vars.parameters.dhwMaxTemp) {
newDHWTemp = constrain(newDHWTemp, vars.parameters.dhwMinTemp, vars.parameters.dhwMaxTemp);
}
@@ -111,6 +122,7 @@ protected:
// Записываем заданную температуру ГВС
if (ot->setDHWSetpoint(newDHWTemp)) {
currentDHWTemp = newDHWTemp;
dhwSetTempTime = millis();
} else {
WARN("Failed set DHW temp");
@@ -119,12 +131,13 @@ protected:
//
// Температура отопления
if (heatingEnable && fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) {
if (heatingEnabled && (needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001)) {
INFO_F("Setting heating temp = %u \n", vars.parameters.heatingSetpoint);
// Записываем заданную температуру
if (ot->setBoilerTemperature(vars.parameters.heatingSetpoint)) {
currentHeatingTemp = vars.parameters.heatingSetpoint;
heatingSetTempTime = millis();
} else {
WARN("Failed set heating temp");
@@ -141,7 +154,7 @@ protected:
} else if (!pump && vars.temperatures.indoor - settings.heating.target - 0.0001 <= -(halfHyst)) {
pump = true;
}
} else if (!pump) {
pump = true;
}
@@ -188,13 +201,27 @@ protected:
}
protected:
unsigned short readyTime = 60000;
unsigned short dhwSetTempInterval = 60000;
unsigned short heatingSetTempInterval = 60000;
bool pump = true;
unsigned long prevUpdateNonEssentialVars = 0;
unsigned long startupTime = millis();
unsigned long dhwSetTempTime = 0;
unsigned long heatingSetTempTime = 0;
bool isReady() {
return millis() - startupTime > 60000;
return millis() - startupTime > readyTime;
}
bool needSetDhwTemp() {
return millis() - dhwSetTempTime > dhwSetTempInterval;
}
bool needSetHeatingTemp() {
return millis() - heatingSetTempTime > heatingSetTempInterval;
}
void static printRequestDetail(OpenThermMessageID id, OpenThermResponseStatus status, unsigned long request, unsigned long response, byte attempt) {
@@ -219,33 +246,33 @@ protected:
//=======================================================================================
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0)); // 0xFFFF
/*uint8_t flags = (response & 0xFFFF) >> 8;
DEBUG_F(
"MasterMemberIdCode:\r\n DHW present: %u\r\n Control type: %u\r\n Cooling configuration: %u\r\n DHW configuration: %u\r\n Pump control: %u\r\n CH2 present: %u\r\n Remote water filling function: %u\r\n Heat/cool mode control: %u\r\n Slave MemberID Code: %u\r\n",
flags & 0x01,
flags & 0x02,
flags & 0x04,
flags & 0x08,
flags & 0x10,
flags & 0x20,
flags & 0x40,
flags & 0x80,
response & 0xFF
);*/
if (ot->isValidResponse(response)) {
vars.parameters.slaveMemberIdCode = response & 0xFF;
} else if ( settings.opentherm.memberIdCode <= 0 ) {
/*uint8_t flags = (response & 0xFFFF) >> 8;
DEBUG_F(
"MasterMemberIdCode:\r\n DHW present: %u\r\n Control type: %u\r\n Cooling configuration: %u\r\n DHW configuration: %u\r\n Pump control: %u\r\n CH2 present: %u\r\n Remote water filling function: %u\r\n Heat/cool mode control: %u\r\n Slave MemberID Code: %u\r\n",
flags & 0x01,
flags & 0x02,
flags & 0x04,
flags & 0x08,
flags & 0x10,
flags & 0x20,
flags & 0x40,
flags & 0x80,
response & 0xFF
);*/
} else if (settings.opentherm.memberIdCode <= 0) {
return false;
}
response = ot->sendRequest(ot->buildRequest(
OpenThermRequestType::WRITE,
OpenThermMessageID::MConfigMMemberIDcode,
settings.opentherm.memberIdCode > 0 ? settings.opentherm.memberIdCode : vars.parameters.slaveMemberIdCode
));
return ot->isValidResponse(response);
}
@@ -306,8 +333,8 @@ protected:
byte maxTemp = (response & 0xFFFF) >> 8;
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
vars.parameters.dhwMinTemp = minTemp;
vars.parameters.dhwMaxTemp = maxTemp;
vars.parameters.dhwMinTemp = minTemp < settings.dhw.minTemp ? settings.dhw.minTemp : minTemp;
vars.parameters.dhwMaxTemp = maxTemp > settings.dhw.maxTemp ? settings.dhw.maxTemp : maxTemp;
return true;
}
@@ -325,8 +352,8 @@ protected:
byte maxTemp = (response & 0xFFFF) >> 8;
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
vars.parameters.heatingMinTemp = minTemp;
vars.parameters.heatingMaxTemp = maxTemp;
vars.parameters.heatingMinTemp = minTemp < settings.heating.minTemp ? settings.heating.minTemp : minTemp;
vars.parameters.heatingMaxTemp = maxTemp > settings.heating.maxTemp ? settings.heating.maxTemp : maxTemp;
return true;
}
@@ -340,7 +367,7 @@ protected:
return false;
}
vars.temperatures.outdoor = ot->getFloat(response);
vars.temperatures.outdoor = ot->getFloat(response) + settings.sensors.outdoor.offset;
return true;
}

View File

@@ -3,12 +3,12 @@
#include <PIDtuner.h>
Equitherm etRegulator;
GyverPID pidRegulator(0, 0, 0, 10000);
GyverPID pidRegulator(0, 0, 0);
PIDtuner pidTuner;
class RegulatorTask: public LeanTask {
class RegulatorTask : public LeanTask {
public:
RegulatorTask(bool _enabled = false, unsigned long _interval = 0): LeanTask(_enabled, _interval) {}
RegulatorTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {}
protected:
bool tunerInit = false;
@@ -73,7 +73,7 @@ protected:
float newTemp = 0;
// if use equitherm
if (settings.emergency.useEquitherm && settings.outdoorTempSource != 1) {
if (settings.emergency.useEquitherm && settings.sensors.outdoor.type != 1) {
float etResult = getEquithermTemp(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp);
if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) {
@@ -123,21 +123,24 @@ protected:
}
// if use pid
if (settings.pid.enable) {
if (settings.pid.enable && vars.parameters.heatingEnabled) {
float pidResult = getPidTemp(
settings.equitherm.enable ? -30 : vars.parameters.heatingMinTemp,
settings.equitherm.enable ? 30 : vars.parameters.heatingMaxTemp
settings.equitherm.enable ? (settings.pid.maxTemp * -1) : settings.pid.minTemp,
settings.equitherm.enable ? settings.pid.maxTemp : settings.pid.maxTemp
);
if (fabs(prevPidResult - pidResult) + 0.0001 >= 0.5) {
prevPidResult = pidResult;
newTemp += pidResult;
INFO_F("[REGULATOR][PID] New result: %u (%f) \n", (int)round(pidResult), pidResult);
INFO_F("[REGULATOR][PID] New result: %d (%f) \n", (int)round(pidResult), pidResult);
} else {
newTemp += prevPidResult;
}
} else if (settings.pid.enable && !vars.parameters.heatingEnabled && prevPidResult != 0) {
newTemp += prevPidResult;
}
// default temp, manual mode
@@ -145,7 +148,9 @@ protected:
newTemp = settings.heating.target;
}
return round(newTemp);
newTemp = round(newTemp);
newTemp = constrain(newTemp, 0, 100);
return newTemp;
}
byte getTuningModeTemp() {
@@ -269,7 +274,7 @@ protected:
pidRegulator.input = vars.temperatures.indoor;
pidRegulator.setpoint = settings.heating.target;
return pidRegulator.getResultTimer();
return pidRegulator.getResultNow();
}
float tuneEquithermN(float ratio, float currentTemp, float setTemp, unsigned int dirtyInterval = 60, unsigned int accurateInterval = 1800, float accurateStep = 0.01, float accurateStepAfter = 1) {

View File

@@ -1,45 +1,149 @@
#include <microDS18B20.h>
#include <OneWire.h>
#include <DallasTemperature.h>
MicroDS18B20<DS18B20_PIN> outdoorSensor;
class SensorsTask: public LeanTask {
class SensorsTask : public LeanTask {
public:
SensorsTask(bool _enabled = false, unsigned long _interval = 0): LeanTask(_enabled, _interval) {}
SensorsTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {}
protected:
OneWire* oneWireOutdoorSensor;
OneWire* oneWireIndoorSensor;
DallasTemperature* outdoorSensor;
DallasTemperature* indoorSensor;
bool initOutdoorSensor = false;
unsigned long startConversionTime = 0;
float filteredOutdoorTemp = 0;
bool emptyOutdoorTemp = true;
bool initIndoorSensor = false;
float filteredIndoorTemp = 0;
bool emptyIndoorTemp = true;
void setup() {}
void loop() {
// DS18B20 sensor
if (outdoorSensor.online()) {
if (outdoorSensor.readTemp()) {
float rawTemp = outdoorSensor.getTemp();
DEBUG_F("[SENSORS][DS18B20] Raw temp: %f \n", rawTemp);
if (settings.sensors.outdoor.type == 2) {
outdoorTemperatureSensor();
}
if (emptyOutdoorTemp) {
filteredOutdoorTemp = rawTemp;
emptyOutdoorTemp = false;
} else {
filteredOutdoorTemp += (rawTemp - filteredOutdoorTemp) * OUTDOOR_SENSOR_FILTER_K;
}
filteredOutdoorTemp = floor(filteredOutdoorTemp * 100) / 100;
if (fabs(vars.temperatures.outdoor - filteredOutdoorTemp) > 0.099) {
vars.temperatures.outdoor = filteredOutdoorTemp;
INFO_F("[SENSORS][DS18B20] New temp: %f \n", filteredOutdoorTemp);
}
} else {
ERROR("[SENSORS][DS18B20] Invalid data from sensor");
}
outdoorSensor.requestTemp();
} else {
ERROR("[SENSORS][DS18B20] Failed to connect to sensor");
if (settings.sensors.indoor.type == 2) {
indoorTemperatureSensor();
}
}
void outdoorTemperatureSensor() {
if (!initOutdoorSensor) {
oneWireOutdoorSensor = new OneWire(settings.sensors.outdoor.pin);
outdoorSensor = new DallasTemperature(oneWireOutdoorSensor);
outdoorSensor->begin();
outdoorSensor->setResolution(12);
outdoorSensor->setWaitForConversion(false);
outdoorSensor->requestTemperatures();
startConversionTime = millis();
initOutdoorSensor = true;
}
unsigned long estimateConversionTime = millis() - startConversionTime;
if (estimateConversionTime < outdoorSensor->millisToWaitForConversion()) {
return;
}
bool completed = outdoorSensor->isConversionComplete();
if (!completed && estimateConversionTime >= 1000) {
// fail, retry
outdoorSensor->requestTemperatures();
startConversionTime = millis();
ERROR("[SENSORS][OUTDOOR] Could not read temperature data (no response)");
}
if (!completed) {
return;
}
float rawTemp = outdoorSensor->getTempCByIndex(0);
if (rawTemp == DEVICE_DISCONNECTED_C) {
ERROR("[SENSORS][OUTDOOR] Could not read temperature data (not connected)");
} else {
DEBUG_F("[SENSORS][OUTDOOR] Raw temp: %f \n", rawTemp);
if (emptyOutdoorTemp) {
filteredOutdoorTemp = rawTemp;
emptyOutdoorTemp = false;
} else {
filteredOutdoorTemp += (rawTemp - filteredOutdoorTemp) * EXT_SENSORS_FILTER_K;
}
filteredOutdoorTemp = floor(filteredOutdoorTemp * 100) / 100;
if (fabs(vars.temperatures.outdoor - filteredOutdoorTemp) > 0.099) {
vars.temperatures.outdoor = filteredOutdoorTemp + settings.sensors.outdoor.offset;
INFO_F("[SENSORS][OUTDOOR] New temp: %f \n", filteredOutdoorTemp);
}
}
outdoorSensor->requestTemperatures();
startConversionTime = millis();
}
void indoorTemperatureSensor() {
if (!initIndoorSensor) {
oneWireIndoorSensor = new OneWire(settings.sensors.indoor.pin);
indoorSensor = new DallasTemperature(oneWireIndoorSensor);
indoorSensor->begin();
indoorSensor->setResolution(12);
indoorSensor->setWaitForConversion(false);
indoorSensor->requestTemperatures();
startConversionTime = millis();
initIndoorSensor = true;
}
unsigned long estimateConversionTime = millis() - startConversionTime;
if (estimateConversionTime < indoorSensor->millisToWaitForConversion()) {
return;
}
bool completed = indoorSensor->isConversionComplete();
if (!completed && estimateConversionTime >= 1000) {
// fail, retry
indoorSensor->requestTemperatures();
startConversionTime = millis();
ERROR("[SENSORS][INDOOR] Could not read temperature data (no response)");
}
if (!completed) {
return;
}
float rawTemp = indoorSensor->getTempCByIndex(0);
if (rawTemp == DEVICE_DISCONNECTED_C) {
ERROR("[SENSORS][INDOOR] Could not read temperature data (not connected)");
} else {
DEBUG_F("[SENSORS][INDOOR] Raw temp: %f \n", rawTemp);
if (emptyIndoorTemp) {
filteredIndoorTemp = rawTemp;
emptyIndoorTemp = false;
} else {
filteredIndoorTemp += (rawTemp - filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
}
filteredIndoorTemp = floor(filteredIndoorTemp * 100) / 100;
if (fabs(vars.temperatures.indoor - filteredIndoorTemp) > 0.099) {
vars.temperatures.indoor = filteredIndoorTemp + settings.sensors.indoor.offset;
INFO_F("[SENSORS][INDOOR] New temp: %f \n", filteredIndoorTemp);
}
}
indoorSensor->requestTemperatures();
startConversionTime = millis();
}
};

View File

@@ -1,13 +1,12 @@
struct Settings {
bool debug = false;
// 0 - boiler, 1 - manual, 2 - ds18b20
byte outdoorTempSource = 0;
char hostname[80] = "opentherm";
struct {
byte inPin = 5;
byte outPin = 4;
byte inPin = 4;
byte outPin = 5;
unsigned int memberIdCode = 0;
bool dhwPresent = true;
} opentherm;
struct {
@@ -30,11 +29,15 @@ struct Settings {
bool turbo = false;
float target = 40.0f;
float hysteresis = 0.5f;
byte minTemp = 20.0f;
byte maxTemp = 90.0f;
} heating;
struct {
bool enable = true;
byte target = 40;
byte minTemp = 30.0f;
byte maxTemp = 60.0f;
} dhw;
struct {
@@ -42,6 +45,8 @@ struct Settings {
float p_factor = 3;
float i_factor = 0.2f;
float d_factor = 0;
byte minTemp = 0.0f;
byte maxTemp = 90.0f;
} pid;
struct {
@@ -51,6 +56,23 @@ struct Settings {
float t_factor = 2.0f;
} equitherm;
struct {
struct {
// 0 - boiler, 1 - manual, 2 - ds18b20
byte type = 0;
byte pin = 12;
float offset = 0.0f;
} outdoor;
struct {
// 1 - manual, 2 - ds18b20
byte type = 1;
byte pin = 14;
float offset = 0.0f;
} indoor;
} sensors;
char validationValue[8] = SETTINGS_VALID_VALUE;
} settings;
struct Variables {
@@ -84,6 +106,7 @@ struct Variables {
} temperatures;
struct {
bool heatingEnabled = false;
byte heatingMinTemp = 20;
byte heatingMaxTemp = 90;
byte heatingSetpoint = 0.0f;

View File

@@ -1,20 +1,28 @@
#include <WiFiManager.h>
#include <WiFiManagerParameters.h>
// Wifimanager
WiFiManager wm;
WiFiManagerParameter* wmHostname;
WiFiManagerParameter* wmOtInPin;
WiFiManagerParameter* wmOtOutPin;
WiFiManagerParameter* wmOtMemberIdCode;
WiFiManagerParameter* wmMqttServer;
WiFiManagerParameter* wmMqttPort;
IntParameter* wmMqttPort;
WiFiManagerParameter* wmMqttUser;
WiFiManagerParameter* wmMqttPassword;
WiFiManagerParameter* wmMqttPrefix;
IntParameter* wmMqttPublishInterval;
IntParameter* wmOtInPin;
IntParameter* wmOtOutPin;
IntParameter* wmOtMemberIdCode;
CheckboxParameter* wmOtDHWPresent;
IntParameter* wmOutdoorSensorPin;
IntParameter* wmIndoorSensorPin;
class WifiManagerTask: public Task {
SeparatorParameter* wmSep1;
SeparatorParameter* wmSep2;
class WifiManagerTask : public Task {
public:
WifiManagerTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {}
WifiManagerTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
protected:
void setup() {
@@ -24,34 +32,48 @@ protected:
wmHostname = new WiFiManagerParameter("hostname", "Hostname", settings.hostname, 80);
wm.addParameter(wmHostname);
sprintf(buffer, "%d", settings.opentherm.inPin);
wmOtInPin = new WiFiManagerParameter("ot_in_pin", "Opentherm pin IN", buffer, 1);
wm.addParameter(wmOtInPin);
sprintf(buffer, "%d", settings.opentherm.outPin);
wmOtOutPin = new WiFiManagerParameter("ot_out_pin", "Opentherm pin OUT", buffer, 1);
wm.addParameter(wmOtOutPin);
sprintf(buffer, "%d", settings.opentherm.memberIdCode);
wmOtMemberIdCode = new WiFiManagerParameter("ot_member_id_code", "Opentherm member id code", buffer, 5);
wm.addParameter(wmOtMemberIdCode);
wmMqttServer = new WiFiManagerParameter("mqtt_server", "MQTT server", settings.mqtt.server, 80);
wm.addParameter(wmMqttServer);
sprintf(buffer, "%d", settings.mqtt.port);
wmMqttPort = new WiFiManagerParameter("mqtt_port", "MQTT port", buffer, 6);
wmMqttPort = new IntParameter("mqtt_port", "MQTT port", settings.mqtt.port, 6);
wm.addParameter(wmMqttPort);
wmMqttUser = new WiFiManagerParameter("mqtt_user", "MQTT username", settings.mqtt.user, 32);
wm.addParameter(wmMqttUser);
wmMqttPassword = new WiFiManagerParameter("mqtt_password", "MQTT password", settings.mqtt.password, 32);
wmMqttPassword = new WiFiManagerParameter("mqtt_password", "MQTT password", settings.mqtt.password, 32, "type=\"password\"");
wm.addParameter(wmMqttPassword);
wmMqttPrefix = new WiFiManagerParameter("mqtt_prefix", "MQTT prefix", settings.mqtt.prefix, 32);
wm.addParameter(wmMqttPrefix);
wmMqttPublishInterval = new IntParameter("mqtt_publish_interval", "MQTT publish interval", settings.mqtt.interval, 5);
wm.addParameter(wmMqttPublishInterval);
wmSep1 = new SeparatorParameter();
wm.addParameter(wmSep1);
wmOtInPin = new IntParameter("ot_in_pin", "Opentherm pin IN", settings.opentherm.inPin, 2);
wm.addParameter(wmOtInPin);
wmOtOutPin = new IntParameter("ot_out_pin", "Opentherm pin OUT", settings.opentherm.outPin, 2);
wm.addParameter(wmOtOutPin);
wmOtMemberIdCode = new IntParameter("ot_member_id_code", "Opentherm member id", settings.opentherm.memberIdCode, 5);
wm.addParameter(wmOtMemberIdCode);
wmOtDHWPresent = new CheckboxParameter("ot_dhw_present", "Opentherm DHW present", settings.opentherm.dhwPresent);
wm.addParameter(wmOtDHWPresent);
wmSep2 = new SeparatorParameter();
wm.addParameter(wmSep2);
wmOutdoorSensorPin = new IntParameter("outdoor_sensor_pin", "Outdoor sensor pin", settings.sensors.outdoor.pin, 2);
wm.addParameter(wmOutdoorSensorPin);
wmIndoorSensorPin = new IntParameter("indoor_sensor_pin", "Indoor sensor pin", settings.sensors.indoor.pin, 2);
wm.addParameter(wmIndoorSensorPin);
//wm.setCleanConnect(true);
wm.setRestorePersistent(false);
@@ -94,32 +116,46 @@ protected:
void static saveParamsCallback() {
strcpy(settings.hostname, wmHostname->getValue());
settings.opentherm.inPin = atoi(wmOtInPin->getValue());
settings.opentherm.outPin = atoi(wmOtOutPin->getValue());
settings.opentherm.memberIdCode = atoi(wmOtMemberIdCode->getValue());
strcpy(settings.mqtt.server, wmMqttServer->getValue());
settings.mqtt.port = atoi(wmMqttPort->getValue());
settings.mqtt.port = wmMqttPort->getValue();
strcpy(settings.mqtt.user, wmMqttUser->getValue());
strcpy(settings.mqtt.password, wmMqttPassword->getValue());
strcpy(settings.mqtt.prefix, wmMqttPrefix->getValue());
settings.mqtt.interval = wmMqttPublishInterval->getValue();
settings.opentherm.inPin = wmOtInPin->getValue();
settings.opentherm.outPin = wmOtOutPin->getValue();
settings.opentherm.memberIdCode = wmOtMemberIdCode->getValue();
settings.opentherm.dhwPresent = wmOtDHWPresent->getCheckboxValue();
settings.sensors.outdoor.pin = wmOutdoorSensorPin->getValue();
settings.sensors.indoor.pin = wmIndoorSensorPin->getValue();
INFO_F(
"New settings:\r\n"
" Hostname: %s\r\n"
" OT in pin: %d"
" OT out pin: %d"
" OT member id code: %d"
" Mqtt server: %s:%d\r\n"
" Mqtt user: %s\r\n"
" Mqtt pass: %s\r\n",
" Mqtt pass: %s\r\n"
" Mqtt prefix: %s\r\n"
" Mqtt publish interval: %d\r\n"
" OT in pin: %d\r\n"
" OT out pin: %d\r\n"
" OT member id code: %d\r\n"
" OT DHW present: %d\r\n"
" Outdoor sensor pin: %d\r\n"
" Indoor sensor pin: %d\r\n",
settings.hostname,
settings.opentherm.inPin,
settings.opentherm.outPin,
settings.opentherm.memberIdCode,
settings.mqtt.server,
settings.mqtt.port,
settings.mqtt.user,
settings.mqtt.password
settings.mqtt.password,
settings.mqtt.prefix,
settings.mqtt.interval,
settings.opentherm.inPin,
settings.opentherm.outPin,
settings.opentherm.memberIdCode,
settings.opentherm.dhwPresent,
settings.sensors.outdoor.pin,
settings.sensors.indoor.pin
);
eeSettings.updateNow();
INFO(F("Settings saved"));

View File

@@ -1,4 +1,4 @@
#define OT_GATEWAY_VERSION "1.2.1"
#define OT_GATEWAY_VERSION "1.3.2"
#define AP_SSID "OpenTherm Gateway"
#define AP_PASSWORD "otgateway123456"
#define USE_TELNET
@@ -9,16 +9,14 @@
#define OPENTHERM_OFFLINE_TRESHOLD 10
#define DS18B20_PIN 2
#define DS18B20_INTERVAL 5000
#define OUTDOOR_SENSOR_FILTER_K 0.15
#define DS_CHECK_CRC true
#define DS_CRC_USE_TABLE true
#define EXT_SENSORS_INTERVAL 5000
#define EXT_SENSORS_FILTER_K 0.15
#define LED_STATUS_PIN 13
#define LED_OT_RX_PIN 15
#define CONFIG_URL "http://%s/"
#define SETTINGS_VALID_VALUE "stvalid" // only 8 chars!
#ifdef USE_TELNET

View File

@@ -29,7 +29,7 @@ MainTask* tMain;
void setup() {
#ifdef USE_TELNET
TelnetStream.begin();
delay(5000);
delay(1000);
#else
Serial.begin(115200);
Serial.println("\n\n");
@@ -40,6 +40,13 @@ void setup() {
if (eeSettingsResult == 0) {
INFO("Settings loaded");
if (strcmp(SETTINGS_VALID_VALUE, settings.validationValue) != 0) {
INFO("Settings not valid, reset and restart...");
eeSettings.reset();
delay(1000);
ESP.restart();
}
} else if (eeSettingsResult == 1) {
INFO("Settings NOT loaded, first start");
@@ -56,7 +63,7 @@ void setup() {
tOt = new OpenThermTask(false);
Scheduler.start(tOt);
tSensors = new SensorsTask(false, DS18B20_INTERVAL);
tSensors = new SensorsTask(true, EXT_SENSORS_INTERVAL);
Scheduler.start(tSensors);
tRegulator = new RegulatorTask(true, 10000);