mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 18:54:28 +05:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d3172b73b | ||
|
|
fca6dc9393 | ||
|
|
b54ea9b745 | ||
|
|
5de3238f6f | ||
|
|
2270b12b36 | ||
|
|
fd4fd119da | ||
|
|
47849eab01 | ||
|
|
ef99d2af96 | ||
|
|
826581562a | ||
|
|
d10d44bd13 | ||
|
|
229628fdc5 | ||
|
|
b0e01afecb | ||
|
|
f544baee0a | ||
|
|
3ff68f544f |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,5 +1,2 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
.vscode
|
||||
101
README.md
101
README.md
@@ -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,54 @@
|
||||

|
||||
|
||||
## 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 | - |
|
||||
|
||||
## 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 +111,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 +168,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 +190,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
1
assets/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/*.priv.*
|
||||
BIN
assets/BOM.xlsx
BIN
assets/BOM.xlsx
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 |
38
lib/WiFiManagerParameters/WiFiManagerParameters.h
Normal file
38
lib/WiFiManagerParameters/WiFiManagerParameters.h
Normal 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
4
otgateway.ino
Normal 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
|
||||
*/
|
||||
@@ -21,8 +21,9 @@ 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
|
||||
|
||||
@@ -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,17 +40,17 @@ 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);
|
||||
client.beginPublish((F("homeassistant/select/") + _prefix + F("/outdoor_sensor_type/config")).c_str(), measureJson(doc), true);
|
||||
//BufferingPrint bufferedClient(client, 32);
|
||||
//serializeJson(doc, bufferedClient);
|
||||
//bufferedClient.flush();
|
||||
@@ -58,6 +58,105 @@ public:
|
||||
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);
|
||||
//BufferingPrint bufferedClient(client, 32);
|
||||
//serializeJson(doc, bufferedClient);
|
||||
//bufferedClient.flush();
|
||||
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;
|
||||
@@ -148,6 +247,7 @@ 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);
|
||||
@@ -160,7 +260,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;
|
||||
@@ -282,8 +382,9 @@ public:
|
||||
doc[F("command_topic")] = _prefix + F("/settings/set");
|
||||
doc[F("command_template")] = F("{\"heating\": {\"target\" : {{ value }}}}");
|
||||
doc[F("min")] = minTemp;
|
||||
doc[F("max")] = maxTemp;
|
||||
doc[F("max")] = maxTemp <= minTemp ? maxTemp : 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);
|
||||
@@ -318,6 +419,7 @@ 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);
|
||||
@@ -358,6 +460,126 @@ public:
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
bool publishSwitchDHW(bool enabledByDefault = true) {
|
||||
StaticJsonDocument<1536> doc;
|
||||
@@ -416,8 +638,9 @@ 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);
|
||||
@@ -427,6 +650,126 @@ public:
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
bool publishSwitchPID(bool enabledByDefault = true) {
|
||||
StaticJsonDocument<1536> doc;
|
||||
@@ -482,6 +825,7 @@ 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);
|
||||
@@ -513,6 +857,7 @@ 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);
|
||||
@@ -544,6 +889,7 @@ 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);
|
||||
@@ -553,6 +899,70 @@ public:
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
bool publishSwitchEquitherm(bool enabledByDefault = true) {
|
||||
StaticJsonDocument<1536> doc;
|
||||
@@ -608,6 +1018,7 @@ 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);
|
||||
@@ -639,6 +1050,7 @@ 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);
|
||||
@@ -672,6 +1084,7 @@ 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);
|
||||
@@ -979,7 +1392,7 @@ public:
|
||||
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;
|
||||
@@ -1076,7 +1489,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,8 +1509,10 @@ 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);
|
||||
@@ -1108,6 +1522,38 @@ public:
|
||||
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);
|
||||
//BufferingPrint bufferedClient(client, 32);
|
||||
//serializeJson(doc, bufferedClient);
|
||||
//bufferedClient.flush();
|
||||
serializeJson(doc, client);
|
||||
return client.endPublish();
|
||||
}
|
||||
|
||||
bool publishNumberOutdoorTemp(bool enabledByDefault = true) {
|
||||
StaticJsonDocument<1536> doc;
|
||||
doc[F("device")][F("identifiers")][0] = _prefix;
|
||||
@@ -1130,8 +1576,10 @@ 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);
|
||||
@@ -1144,8 +1592,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;
|
||||
@@ -1353,6 +1799,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";
|
||||
|
||||
@@ -13,7 +13,6 @@ protected:
|
||||
|
||||
void setup() {
|
||||
pinMode(LED_STATUS_PIN, OUTPUT);
|
||||
//pinMode(LED_OT_RX_PIN, OUTPUT);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
284
src/MqttTask.h
284
src/MqttTask.h
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -9,6 +9,11 @@ public:
|
||||
|
||||
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);
|
||||
@@ -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) {
|
||||
@@ -80,28 +92,28 @@ protected:
|
||||
}
|
||||
|
||||
updatePressure();
|
||||
if ( settings.dhw.enable || settings.heating.enable || heatingEnable ) {
|
||||
if ((settings.opentherm.dhwPresent && settings.dhw.enable) || settings.heating.enable || heatingEnabled ) {
|
||||
updateModulationLevel();
|
||||
}
|
||||
yield();
|
||||
|
||||
if ( settings.dhw.enable ) {
|
||||
if ( settings.opentherm.dhwPresent && settings.dhw.enable ) {
|
||||
updateDHWTemp();
|
||||
} else {
|
||||
vars.temperatures.dhw = 0;
|
||||
}
|
||||
|
||||
if ( settings.heating.enable || heatingEnable ) {
|
||||
//if ( settings.heating.enable || heatingEnabled ) {
|
||||
updateHeatingTemp();
|
||||
} else {
|
||||
vars.temperatures.heating = 0;
|
||||
}
|
||||
|
||||
//} else {
|
||||
// vars.temperatures.heating = 0;
|
||||
//}
|
||||
yield();
|
||||
|
||||
//
|
||||
// Температура ГВС
|
||||
byte newDHWTemp = settings.dhw.target;
|
||||
if (settings.dhw.enable && newDHWTemp != currentDHWTemp) {
|
||||
if (settings.opentherm.dhwPresent && settings.dhw.enable && newDHWTemp != currentDHWTemp) {
|
||||
if (newDHWTemp < vars.parameters.dhwMinTemp || newDHWTemp > vars.parameters.dhwMaxTemp) {
|
||||
newDHWTemp = constrain(newDHWTemp, vars.parameters.dhwMinTemp, vars.parameters.dhwMaxTemp);
|
||||
}
|
||||
@@ -119,7 +131,7 @@ protected:
|
||||
|
||||
//
|
||||
// Температура отопления
|
||||
if (heatingEnable && fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) {
|
||||
if (heatingEnabled && fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) {
|
||||
INFO_F("Setting heating temp = %u \n", vars.parameters.heatingSetpoint);
|
||||
|
||||
// Записываем заданную температуру
|
||||
@@ -219,23 +231,23 @@ 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;
|
||||
|
||||
/*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;
|
||||
}
|
||||
@@ -306,8 +318,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 +337,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 +352,7 @@ protected:
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.temperatures.outdoor = ot->getFloat(response);
|
||||
vars.temperatures.outdoor = ot->getFloat(response) + settings.sensors.outdoor.offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <PIDtuner.h>
|
||||
|
||||
Equitherm etRegulator;
|
||||
GyverPID pidRegulator(0, 0, 0, 10000);
|
||||
GyverPID pidRegulator(0, 0, 0);
|
||||
PIDtuner pidTuner;
|
||||
|
||||
class RegulatorTask: public LeanTask {
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,45 +1,149 @@
|
||||
#include <microDS18B20.h>
|
||||
|
||||
MicroDS18B20<DS18B20_PIN> outdoorSensor;
|
||||
#include <OneWire.h>
|
||||
#include <DallasTemperature.h>
|
||||
|
||||
class SensorsTask: public LeanTask {
|
||||
public:
|
||||
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();
|
||||
}
|
||||
};
|
||||
@@ -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,11 +106,12 @@ struct Variables {
|
||||
} temperatures;
|
||||
|
||||
struct {
|
||||
byte heatingMinTemp = 20;
|
||||
byte heatingMaxTemp = 90;
|
||||
bool heatingEnabled = false;
|
||||
byte heatingMinTemp = 0;
|
||||
byte heatingMaxTemp = 0;
|
||||
byte heatingSetpoint = 0.0f;
|
||||
byte dhwMinTemp = 30;
|
||||
byte dhwMaxTemp = 60;
|
||||
byte dhwMinTemp = 0;
|
||||
byte dhwMaxTemp = 0;
|
||||
uint8_t slaveMemberIdCode;
|
||||
uint8_t slaveType;
|
||||
uint8_t slaveVersion;
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#define OT_GATEWAY_VERSION "1.2.1"
|
||||
#define OT_GATEWAY_VERSION "1.3.1"
|
||||
#define AP_SSID "OpenTherm Gateway"
|
||||
#define AP_PASSWORD "otgateway123456"
|
||||
#define USE_TELNET
|
||||
@@ -9,9 +9,8 @@
|
||||
|
||||
#define OPENTHERM_OFFLINE_TRESHOLD 10
|
||||
|
||||
#define DS18B20_PIN 2
|
||||
#define DS18B20_INTERVAL 5000
|
||||
#define OUTDOOR_SENSOR_FILTER_K 0.15
|
||||
#define EXT_SENSORS_INTERVAL 5000
|
||||
#define EXT_SENSORS_FILTER_K 0.15
|
||||
#define DS_CHECK_CRC true
|
||||
#define DS_CRC_USE_TABLE true
|
||||
|
||||
@@ -19,6 +18,7 @@
|
||||
#define LED_OT_RX_PIN 15
|
||||
|
||||
#define CONFIG_URL "http://%s/"
|
||||
#define SETTINGS_VALID_VALUE "stvalid" // only 8 chars!
|
||||
|
||||
|
||||
#ifdef USE_TELNET
|
||||
|
||||
11
src/main.cpp
11
src/main.cpp
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user