14 Commits

Author SHA1 Message Date
Yurii
7cbc52a8b0 chore: bump version 2024-03-01 00:26:41 +03:00
Laxilef
e090be380c Merge pull request #49 from blitzu/patch-3
fix: fix typo in settings.html
2024-03-01 00:22:32 +03:00
Laxilef
0bf49d2249 Merge pull request #50 from blitzu/patch-2
fix: fix typo in network.html
2024-03-01 00:22:13 +03:00
Laxilef
a83d94d361 Merge pull request #51 from blitzu/patch-1
fix: fix typo in index.html
2024-03-01 00:21:49 +03:00
Laxilef
358980da4c Merge pull request #48 from blitzu/patch-4
fix: fix typo in upgrade.html
2024-03-01 00:21:13 +03:00
blitzu
f91e39d067 Update upgrade.html 2024-02-29 18:52:31 +02:00
blitzu
1d53f21d46 Update settings.html 2024-02-29 18:52:08 +02:00
blitzu
c225e7c2a8 Update network.html 2024-02-29 18:51:25 +02:00
blitzu
6831c4331f Update index.html 2024-02-29 18:50:36 +02:00
Yurii
8fb62ce8ae fix: set temperature for sensors in manual mode fixed 2024-02-23 03:50:30 +03:00
Yurii
e829a00355 chore: bump version 2024-02-20 16:21:20 +03:00
Yurii
bee720386a refactor: changed availability conditions for HA entities 2024-02-20 16:17:03 +03:00
Yurii
c4b6eadb81 refactor: using BLE advertising instead of manual requests 2024-02-20 15:29:14 +03:00
Yurii
a5d2b9fcfa refactor: small fixes 2024-02-20 15:27:51 +03:00
19 changed files with 259 additions and 227 deletions

View File

@@ -16,7 +16,7 @@
- PID - PID
- Equithermic curves - adjusts the temperature based on indoor and outdoor temperatures - Equithermic curves - adjusts the temperature based on indoor and outdoor temperatures
- Hysteresis setting (for accurate maintenance of room temperature) - Hysteresis setting (for accurate maintenance of room temperature)
- Ability to connect an external sensors to monitor outdoor and indoor temperature ([compatible sensors](#compatible-temperature-sensors)) - Ability to connect an external sensors to monitor outdoor and indoor temperature ([compatible sensors](https://github.com/Laxilef/OTGateway/wiki/Compatibility#temperature-sensors))
- Emergency mode. If the Wi-Fi connection is lost or the gateway cannot connect to the MQTT server, the mode will turn on. This mode will automatically maintain the set temperature and prevent your home from freezing. In this mode it is also possible to use equithermal curves (weather-compensated control). - 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) - Automatic error reset (not with all boilers)
- Diagnostics: - Diagnostics:
@@ -72,7 +72,7 @@ All available information and instructions can be found in the wiki:
- [ESP32Scheduler](https://github.com/laxilef/ESP32Scheduler) (for ESP32) - [ESP32Scheduler](https://github.com/laxilef/ESP32Scheduler) (for ESP32)
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson) - [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
- [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library) - [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library)
- [ArduinoMqttClient](https://github.com/arduino-libraries/ArduinoMqttClient) / - [ArduinoMqttClient](https://github.com/arduino-libraries/ArduinoMqttClient)
- [ESPTelnet](https://github.com/LennartHennigs/ESPTelnet) - [ESPTelnet](https://github.com/LennartHennigs/ESPTelnet)
- [FileData](https://github.com/GyverLibs/FileData) - [FileData](https://github.com/GyverLibs/FileData)
- [GyverPID](https://github.com/GyverLibs/GyverPID) - [GyverPID](https://github.com/GyverLibs/GyverPID)

View File

@@ -210,7 +210,7 @@
<a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a> <a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a>
<a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a> <a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a>
<a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a> <a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a>
<a href="https://github.com/Laxilef/OTGateway/issue" target="_blank" class="secondary">Issue & questions</a> <a href="https://github.com/Laxilef/OTGateway/issues" target="_blank" class="secondary">Issue & questions</a>
<a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a> <a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a>
</small> </small>
</footer> </footer>

View File

@@ -145,7 +145,7 @@
<a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a> <a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a>
<a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a> <a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a>
<a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a> <a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a>
<a href="https://github.com/Laxilef/OTGateway/issue" target="_blank" class="secondary">Issue & questions</a> <a href="https://github.com/Laxilef/OTGateway/issues" target="_blank" class="secondary">Issue & questions</a>
<a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a> <a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a>
</small> </small>
</footer> </footer>

View File

@@ -333,7 +333,7 @@
<a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a> <a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a>
<a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a> <a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a>
<a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a> <a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a>
<a href="https://github.com/Laxilef/OTGateway/issue" target="_blank" class="secondary">Issue & questions</a> <a href="https://github.com/Laxilef/OTGateway/issues" target="_blank" class="secondary">Issue & questions</a>
<a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a> <a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a>
</small> </small>
</footer> </footer>

View File

@@ -91,7 +91,7 @@
<a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a> <a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a>
<a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a> <a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a>
<a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a> <a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a>
<a href="https://github.com/Laxilef/OTGateway/issue" target="_blank" class="secondary">Issue & questions</a> <a href="https://github.com/Laxilef/OTGateway/issues" target="_blank" class="secondary">Issue & questions</a>
<a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a> <a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a>
</small> </small>
</footer> </footer>

View File

@@ -17,7 +17,7 @@ public:
float Kk = 0.0; float Kk = 0.0;
float Kt = 0.0; float Kt = 0.0;
Equitherm() {} Equitherm() = default;
// kn, kk, kt // kn, kk, kt
Equitherm(float new_kn, float new_kk, float new_kt) { Equitherm(float new_kn, float new_kk, float new_kt) {

View File

@@ -6,7 +6,7 @@ class HomeAssistantHelper {
public: public:
typedef std::function<void(const char*, bool)> PublishEventCallback; typedef std::function<void(const char*, bool)> PublishEventCallback;
HomeAssistantHelper() {} HomeAssistantHelper() = default;
void setWriter() { void setWriter() {
this->writer = nullptr; this->writer = nullptr;

View File

@@ -177,7 +177,6 @@ public:
} else { } else {
sizeArgName = length - size_t(argStartPos - currentBuf) - 1; sizeArgName = length - size_t(argStartPos - currentBuf) - 1;
Serial.printf("sizeArgName: %d\r\n", sizeArgName);
// send all content if arg len > space // send all content if arg len > space
if (sizeArgName >= sizeof(argName)) { if (sizeArgName >= sizeof(argName)) {

View File

@@ -51,7 +51,7 @@ monitor_speed = 115200
monitor_filters = direct monitor_filters = direct
board_build.flash_mode = dio board_build.flash_mode = dio
board_build.filesystem = littlefs board_build.filesystem = littlefs
version = 1.4.0-rc.14 version = 1.4.0-rc.16
; Defaults ; Defaults
[esp8266_defaults] [esp8266_defaults]

View File

@@ -6,59 +6,6 @@ public:
static const byte TEMP_SOURCE_HEATING = 0; static const byte TEMP_SOURCE_HEATING = 0;
static const byte TEMP_SOURCE_INDOOR = 1; static const byte TEMP_SOURCE_INDOOR = 1;
bool publishNumberOutdoorSensorOffset(bool enabledByDefault = true) {
JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.sensors.outdoor.type != 1, 'online', 'offline') }}");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("outdoor_sensor_offset"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("outdoor_sensor_offset"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
doc[FPSTR(HA_NAME)] = F("Outdoor sensor offset");
doc[FPSTR(HA_ICON)] = F("mdi:altimeter");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.sensors.outdoor.offset|float(0)|round(2) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"sensors\": {\"outdoor\" : {\"offset\" : {{ value }}}}}");
doc[FPSTR(HA_MIN)] = -10;
doc[FPSTR(HA_MAX)] = 10;
doc[FPSTR(HA_STEP)] = 0.1;
doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
doc.shrinkToFit();
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("outdoor_sensor_offset")).c_str(), doc);
}
bool publishNumberIndoorSensorOffset(bool enabledByDefault = true) {
JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.sensors.indoor.type != 1, 'online', 'offline') }}");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("indoor_sensor_offset"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("indoor_sensor_offset"));
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
doc[FPSTR(HA_NAME)] = F("Indoor sensor offset");
doc[FPSTR(HA_ICON)] = F("mdi:altimeter");
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.sensors.indoor.offset|float(0)|round(2) }}");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"sensors\": {\"indoor\" : {\"offset\" : {{ value }}}}}");
doc[FPSTR(HA_MIN)] = -10;
doc[FPSTR(HA_MAX)] = 10;
doc[FPSTR(HA_STEP)] = 0.1;
doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
doc.shrinkToFit();
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("indoor_sensor_offset")).c_str(), doc);
}
bool publishSwitchEmergency(bool enabledByDefault = true) { bool publishSwitchEmergency(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
@@ -266,7 +213,10 @@ public:
bool publishSensorBoilerHeatingMinTemp(bool enabledByDefault = true) { bool publishSensorBoilerHeatingMinTemp(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_heating_min_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_heating_min_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_heating_min_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_heating_min_temp"));
@@ -286,7 +236,10 @@ public:
bool publishSensorBoilerHeatingMaxTemp(bool enabledByDefault = true) { bool publishSensorBoilerHeatingMaxTemp(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_heating_max_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_heating_max_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_heating_max_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_heating_max_temp"));
@@ -415,7 +368,7 @@ public:
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"dhw\": {\"target\" : {{ value|int(0) }}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"dhw\": {\"target\" : {{ value|int(0) }}}}");
doc[FPSTR(HA_MIN)] = minTemp; doc[FPSTR(HA_MIN)] = minTemp;
doc[FPSTR(HA_MAX)] = maxTemp <= minTemp ? maxTemp : maxTemp; doc[FPSTR(HA_MAX)] = maxTemp > minTemp ? maxTemp : minTemp;
doc[FPSTR(HA_STEP)] = 1; doc[FPSTR(HA_STEP)] = 1;
doc[FPSTR(HA_MODE)] = "box"; doc[FPSTR(HA_MODE)] = "box";
doc[FPSTR(HA_EXPIRE_AFTER)] = 120; doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
@@ -426,7 +379,10 @@ public:
bool publishSensorBoilerDhwMinTemp(bool enabledByDefault = true) { bool publishSensorBoilerDhwMinTemp(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_dhw_min_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_dhw_min_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_dhw_min_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_dhw_min_temp"));
@@ -446,7 +402,10 @@ public:
bool publishSensorBoilerDhwMaxTemp(bool enabledByDefault = true) { bool publishSensorBoilerDhwMaxTemp(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_dhw_max_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_dhw_max_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_dhw_max_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_dhw_max_temp"));
@@ -780,7 +739,6 @@ public:
bool publishSelectTuningRegulator(bool enabledByDefault = true) { bool publishSelectTuningRegulator(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("state/set")); doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("state/set"));
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"tuning\": {\"regulator\": {% if value == 'Equitherm' %}0{% elif value == 'PID' %}1{% endif %}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"tuning\": {\"regulator\": {% if value == 'Equitherm' %}0{% elif value == 'PID' %}1{% endif %}}}");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
@@ -835,7 +793,10 @@ public:
bool publishBinSensorHeating(bool enabledByDefault = true) { bool publishBinSensorHeating(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating"));
@@ -853,7 +814,10 @@ public:
bool publishBinSensorDhw(bool enabledByDefault = true) { bool publishBinSensorDhw(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw"));
@@ -871,7 +835,10 @@ public:
bool publishBinSensorFlame(bool enabledByDefault = true) { bool publishBinSensorFlame(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("flame")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("flame"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("flame")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("flame"));
@@ -889,8 +856,10 @@ public:
bool publishBinSensorFault(bool enabledByDefault = true) { bool publishBinSensorFault(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}"); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("fault")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("fault"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("fault")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("fault"));
@@ -908,7 +877,10 @@ public:
bool publishBinSensorDiagnostic(bool enabledByDefault = true) { bool publishBinSensorDiagnostic(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("diagnostic")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("diagnostic"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("diagnostic")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("diagnostic"));
@@ -926,8 +898,10 @@ public:
bool publishSensorFaultCode(bool enabledByDefault = true) { bool publishSensorFaultCode(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.fault, 'online', 'offline') }}"); doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus and value_json.states.fault, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("fault_code")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("fault_code"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("fault_code")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("fault_code"));
@@ -985,7 +959,10 @@ public:
bool publishSensorModulation(bool enabledByDefault = true) { bool publishSensorModulation(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("modulation_level")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("modulation_level"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("modulation_level")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("modulation_level"));
@@ -1005,7 +982,10 @@ public:
bool publishSensorPressure(bool enabledByDefault = true) { bool publishSensorPressure(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("pressure")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("pressure"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("pressure")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("pressure"));
@@ -1025,7 +1005,10 @@ public:
bool publishSensorDhwFlowRate(bool enabledByDefault = true) { bool publishSensorDhwFlowRate(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_flow_rate")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_flow_rate"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_flow_rate")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_flow_rate"));
@@ -1069,8 +1052,7 @@ public:
bool publishSensorIndoorTemp(bool enabledByDefault = true) { bool publishSensorIndoorTemp(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("any");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("indoor_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("indoor_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("indoor_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("indoor_temp"));
@@ -1113,8 +1095,7 @@ public:
bool publishSensorOutdoorTemp(bool enabledByDefault = true) { bool publishSensorOutdoorTemp(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("any");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("outdoor_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("outdoor_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("outdoor_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("outdoor_temp"));
@@ -1134,7 +1115,10 @@ public:
bool publishSensorHeatingTemp(bool enabledByDefault = true) { bool publishSensorHeatingTemp(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_temp"));
@@ -1154,7 +1138,10 @@ public:
bool publishSensorDhwTemp(bool enabledByDefault = true) { bool publishSensorDhwTemp(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_temp")); doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_temp"));
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_temp")); doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_temp"));

View File

@@ -20,14 +20,11 @@ public:
} }
~MainTask() { ~MainTask() {
if (this->blinker != nullptr) {
delete this->blinker; delete this->blinker;
} }
}
protected: protected:
const static byte REASON_PUMP_START_HEATING = 1; enum class PumpStartReason {NONE, HEATING, ANTISTUCK};
const static byte REASON_PUMP_START_ANTISTUCK = 2;
Blinker* blinker = nullptr; Blinker* blinker = nullptr;
bool blinkerInitialized = false; bool blinkerInitialized = false;
@@ -38,7 +35,7 @@ protected:
unsigned long restartSignalTime = 0; unsigned long restartSignalTime = 0;
bool heatingEnabled = false; bool heatingEnabled = false;
unsigned long heatingDisabledTime = 0; unsigned long heatingDisabledTime = 0;
byte externalPumpStartReason; PumpStartReason extPumpStartReason = PumpStartReason::NONE;
unsigned long externalPumpStartTime = 0; unsigned long externalPumpStartTime = 0;
bool telnetStarted = false; bool telnetStarted = false;
@@ -57,12 +54,12 @@ protected:
void setup() { void setup() {
#ifdef LED_STATUS_PIN #ifdef LED_STATUS_PIN
pinMode(LED_STATUS_PIN, OUTPUT); pinMode(LED_STATUS_PIN, OUTPUT);
digitalWrite(LED_STATUS_PIN, false); digitalWrite(LED_STATUS_PIN, LOW);
#endif #endif
if (settings.externalPump.pin != 0) { if (settings.externalPump.pin != 0) {
pinMode(settings.externalPump.pin, OUTPUT); pinMode(settings.externalPump.pin, OUTPUT);
digitalWrite(settings.externalPump.pin, false); digitalWrite(settings.externalPump.pin, LOW);
} }
} }
@@ -250,14 +247,14 @@ protected:
if (!this->blinker->running() && millis() - endBlinkTime >= 5000) { if (!this->blinker->running() && millis() - endBlinkTime >= 5000) {
if (errCount == 0) { if (errCount == 0) {
if (!ledOn) { if (!ledOn) {
digitalWrite(ledPin, true); digitalWrite(ledPin, HIGH);
ledOn = true; ledOn = true;
} }
return; return;
} else if (ledOn) { } else if (ledOn) {
digitalWrite(ledPin, false); digitalWrite(ledPin, LOW);
ledOn = false; ledOn = false;
endBlinkTime = millis(); endBlinkTime = millis();
return; return;
@@ -289,7 +286,7 @@ protected:
if (!settings.externalPump.use || settings.externalPump.pin == 0) { if (!settings.externalPump.use || settings.externalPump.pin == 0) {
if (vars.states.externalPump) { if (vars.states.externalPump) {
if (settings.externalPump.pin != 0) { if (settings.externalPump.pin != 0) {
digitalWrite(settings.externalPump.pin, false); digitalWrite(settings.externalPump.pin, LOW);
} }
vars.states.externalPump = false; vars.states.externalPump = false;
@@ -302,16 +299,16 @@ protected:
} }
if (vars.states.externalPump && !this->heatingEnabled) { if (vars.states.externalPump && !this->heatingEnabled) {
if (this->externalPumpStartReason == MainTask::REASON_PUMP_START_HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) { if (this->extPumpStartReason == MainTask::PumpStartReason::HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) {
digitalWrite(settings.externalPump.pin, false); digitalWrite(settings.externalPump.pin, LOW);
vars.states.externalPump = false; vars.states.externalPump = false;
vars.parameters.extPumpLastEnableTime = millis(); vars.parameters.extPumpLastEnableTime = millis();
Log.sinfoln("EXTPUMP", F("Disabled: expired post circulation time")); Log.sinfoln("EXTPUMP", F("Disabled: expired post circulation time"));
} else if (this->externalPumpStartReason == MainTask::REASON_PUMP_START_ANTISTUCK && millis() - this->externalPumpStartTime >= (settings.externalPump.antiStuckTime * 1000u)) { } else if (this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK && millis() - this->externalPumpStartTime >= (settings.externalPump.antiStuckTime * 1000u)) {
digitalWrite(settings.externalPump.pin, false); digitalWrite(settings.externalPump.pin, LOW);
vars.states.externalPump = false; vars.states.externalPump = false;
vars.parameters.extPumpLastEnableTime = millis(); vars.parameters.extPumpLastEnableTime = millis();
@@ -319,24 +316,24 @@ protected:
Log.sinfoln("EXTPUMP", F("Disabled: expired anti stuck time")); Log.sinfoln("EXTPUMP", F("Disabled: expired anti stuck time"));
} }
} else if (vars.states.externalPump && this->heatingEnabled && this->externalPumpStartReason == MainTask::REASON_PUMP_START_ANTISTUCK) { } else if (vars.states.externalPump && this->heatingEnabled && this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK) {
this->externalPumpStartReason = MainTask::REASON_PUMP_START_HEATING; this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
} else if (!vars.states.externalPump && this->heatingEnabled) { } else if (!vars.states.externalPump && this->heatingEnabled) {
vars.states.externalPump = true; vars.states.externalPump = true;
this->externalPumpStartTime = millis(); this->externalPumpStartTime = millis();
this->externalPumpStartReason = MainTask::REASON_PUMP_START_HEATING; this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
digitalWrite(settings.externalPump.pin, true); digitalWrite(settings.externalPump.pin, HIGH);
Log.sinfoln("EXTPUMP", F("Enabled: heating on")); Log.sinfoln("EXTPUMP", F("Enabled: heating on"));
} else if (!vars.states.externalPump && (vars.parameters.extPumpLastEnableTime == 0 || millis() - vars.parameters.extPumpLastEnableTime >= (settings.externalPump.antiStuckInterval * 1000ul))) { } else if (!vars.states.externalPump && (vars.parameters.extPumpLastEnableTime == 0 || millis() - vars.parameters.extPumpLastEnableTime >= (settings.externalPump.antiStuckInterval * 1000ul))) {
vars.states.externalPump = true; vars.states.externalPump = true;
this->externalPumpStartTime = millis(); this->externalPumpStartTime = millis();
this->externalPumpStartReason = MainTask::REASON_PUMP_START_ANTISTUCK; this->extPumpStartReason = MainTask::PumpStartReason::ANTISTUCK;
digitalWrite(settings.externalPump.pin, true); digitalWrite(settings.externalPump.pin, HIGH);
Log.sinfoln("EXTPUMP", F("Enabled: anti stuck")); Log.sinfoln("EXTPUMP", F("Enabled: anti stuck"));
} }

View File

@@ -15,9 +15,7 @@ public:
} }
~MqttTask() { ~MqttTask() {
if (this->haHelper != nullptr) {
delete this->haHelper; delete this->haHelper;
}
if (this->client != nullptr) { if (this->client != nullptr) {
if (this->client->connected()) { if (this->client->connected()) {
@@ -27,14 +25,9 @@ public:
delete this->client; delete this->client;
} }
if (this->writer != nullptr) {
delete this->writer; delete this->writer;
}
if (this->wifiClient != nullptr) {
delete this->wifiClient; delete this->wifiClient;
} }
}
void disable() { void disable() {
this->client->stop(); this->client->stop();
@@ -208,12 +201,7 @@ protected:
// publish variables and status // publish variables and status
if (this->newConnection || millis() - this->prevPubVarsTime > (settings.mqtt.interval * 1000u)) { if (this->newConnection || millis() - this->prevPubVarsTime > (settings.mqtt.interval * 1000u)) {
this->writer->publish( this->writer->publish(this->haHelper->getDeviceTopic("status").c_str(), "online", false);
this->haHelper->getDeviceTopic("status").c_str(),
!vars.states.otStatus ? "offline" : vars.states.fault ? "fault" : "online",
true
);
this->publishVariables(this->haHelper->getDeviceTopic("state").c_str()); this->publishVariables(this->haHelper->getDeviceTopic("state").c_str());
this->prevPubVarsTime = millis(); this->prevPubVarsTime = millis();
} }
@@ -328,10 +316,6 @@ protected:
} }
void publishHaEntities() { void publishHaEntities() {
// main
this->haHelper->publishNumberOutdoorSensorOffset(false);
this->haHelper->publishNumberIndoorSensorOffset(false);
// emergency // emergency
this->haHelper->publishSwitchEmergency(); this->haHelper->publishSwitchEmergency();
this->haHelper->publishNumberEmergencyTarget(); this->haHelper->publishNumberEmergencyTarget();

View File

@@ -54,9 +54,9 @@ protected:
#ifdef LED_OT_RX_PIN #ifdef LED_OT_RX_PIN
{ {
digitalWrite(LED_OT_RX_PIN, true); digitalWrite(LED_OT_RX_PIN, HIGH);
delayMicroseconds(2000); delayMicroseconds(2000);
digitalWrite(LED_OT_RX_PIN, false); digitalWrite(LED_OT_RX_PIN, LOW);
} }
#endif #endif
} }
@@ -71,7 +71,7 @@ protected:
#ifdef LED_OT_RX_PIN #ifdef LED_OT_RX_PIN
pinMode(LED_OT_RX_PIN, OUTPUT); pinMode(LED_OT_RX_PIN, OUTPUT);
digitalWrite(LED_OT_RX_PIN, false); digitalWrite(LED_OT_RX_PIN, LOW);
#endif #endif
} }

View File

@@ -28,9 +28,7 @@ public:
} }
~PortalTask() { ~PortalTask() {
if (this->bufferedWebServer != nullptr) {
delete this->bufferedWebServer; delete this->bufferedWebServer;
}
if (this->webServer != nullptr) { if (this->webServer != nullptr) {
this->stopWebServer(); this->stopWebServer();

View File

@@ -159,7 +159,7 @@ protected:
if (vars.parameters.heatingEnabled) { if (vars.parameters.heatingEnabled) {
float pidResult = getPidTemp( float pidResult = getPidTemp(
settings.equitherm.enable ? (settings.pid.maxTemp * -1) : settings.pid.minTemp, settings.equitherm.enable ? (settings.pid.maxTemp * -1) : settings.pid.minTemp,
settings.equitherm.enable ? settings.pid.maxTemp : settings.pid.maxTemp settings.pid.maxTemp
); );
if (fabs(prevPidResult - pidResult) + 0.0001 >= 0.5) { if (fabs(prevPidResult - pidResult) + 0.0001 >= 0.5) {

View File

@@ -3,13 +3,6 @@
#if USE_BLE #if USE_BLE
#include <NimBLEDevice.h> #include <NimBLEDevice.h>
// BLE services and characterstics that we are interested in
const uint16_t bleUuidServiceBattery = 0x180F;
const uint16_t bleUuidServiceEnvironment = 0x181AU;
const uint16_t bleUuidCharacteristicBatteryLevel = 0x2A19;
const uint16_t bleUuidCharacteristicTemperature = 0x2A6E;
const uint16_t bleUuidCharacteristicHumidity = 0x2A6F;
#endif #endif
class SensorsTask : public LeanTask { class SensorsTask : public LeanTask {
@@ -25,22 +18,11 @@ public:
} }
~SensorsTask() { ~SensorsTask() {
if (this->outdoorSensor != nullptr) {
delete this->outdoorSensor; delete this->outdoorSensor;
}
if (this->oneWireOutdoorSensor != nullptr) {
delete this->oneWireOutdoorSensor; delete this->oneWireOutdoorSensor;
}
if (this->indoorSensor != nullptr) {
delete this->indoorSensor; delete this->indoorSensor;
}
if (this->oneWireIndoorSensor != nullptr) {
delete this->oneWireIndoorSensor; delete this->oneWireIndoorSensor;
} }
}
protected: protected:
OneWire* oneWireOutdoorSensor = nullptr; OneWire* oneWireOutdoorSensor = nullptr;
@@ -61,9 +43,8 @@ protected:
#if USE_BLE #if USE_BLE
BLEClient* pBleClient = nullptr; BLEClient* pBleClient = nullptr;
BLERemoteService* pBleServiceBattery = nullptr;
BLERemoteService* pBleServiceEnvironment = nullptr;
bool initBleSensor = false; bool initBleSensor = false;
bool initBleNotify = false;
#endif #endif
const char* getTaskName() { const char* getTaskName() {
@@ -79,67 +60,150 @@ protected:
} }
void loop() { void loop() {
bool needUpdateIndoorTemp = false;
bool needUpdateOutdoorTemp = false;
if (settings.sensors.outdoor.type == 2 && settings.sensors.outdoor.pin) { if (settings.sensors.outdoor.type == 2 && settings.sensors.outdoor.pin) {
outdoorTemperatureSensor(); outdoorTemperatureSensor();
needUpdateOutdoorTemp = true;
} }
if (settings.sensors.indoor.type == 2 && settings.sensors.indoor.pin) { if (settings.sensors.indoor.type == 2 && settings.sensors.indoor.pin) {
indoorTemperatureSensor(); indoorTemperatureSensor();
needUpdateIndoorTemp = true;
} }
#if USE_BLE #if USE_BLE
if (settings.sensors.indoor.type == 3 && strlen(settings.sensors.indoor.bleAddresss)) { if (settings.sensors.indoor.type == 3) {
bluetoothSensor(); bluetoothSensor();
needUpdateIndoorTemp = true;
} }
#endif #endif
if (needUpdateOutdoorTemp && fabs(vars.temperatures.outdoor - this->filteredOutdoorTemp) > 0.099) {
vars.temperatures.outdoor = this->filteredOutdoorTemp + settings.sensors.outdoor.offset;
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("New temp: %f"), vars.temperatures.outdoor);
}
if (needUpdateIndoorTemp && fabs(vars.temperatures.indoor - this->filteredIndoorTemp) > 0.099) {
vars.temperatures.indoor = this->filteredIndoorTemp + settings.sensors.indoor.offset;
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("New temp: %f"), vars.temperatures.indoor);
}
} }
#if USE_BLE #if USE_BLE
void bluetoothSensor() { void bluetoothSensor() {
static bool initBleNotify = false;
if (!initBleSensor && millis() > 5000) { if (!initBleSensor && millis() > 5000) {
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Init BLE. Free heap %u bytes", ESP.getFreeHeap()); Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Init BLE"));
BLEDevice::init(""); BLEDevice::init("");
pBleClient = BLEDevice::createClient(); pBleClient = BLEDevice::createClient();
pBleClient->setConnectTimeout(5);
// Connect to the remote BLE Server.
BLEAddress bleServerAddress(std::string(settings.sensors.indoor.bleAddresss));
if (pBleClient->connect(bleServerAddress)) {
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Connected to BLE device at %s", bleServerAddress.toString().c_str());
// Obtain a reference to the services we are interested in
pBleServiceBattery = pBleClient->getService(BLEUUID(bleUuidServiceBattery));
if (pBleServiceBattery == nullptr) {
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Failed to find battery service");
}
pBleServiceEnvironment = pBleClient->getService(BLEUUID(bleUuidServiceEnvironment));
if (pBleServiceEnvironment == nullptr) {
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Failed to find environmental service");
}
} else {
Log.swarningln(FPSTR(L_SENSORS_BLE), "Error connecting to BLE device at %s", bleServerAddress.toString().c_str());
}
initBleSensor = true; initBleSensor = true;
} }
if (pBleClient && pBleClient->isConnected()) { if (!initBleSensor || pBleClient->isConnected()) {
Log.straceln(FPSTR(L_SENSORS_BLE), "Connected. Free heap %u bytes", ESP.getFreeHeap()); return;
if (pBleServiceBattery) {
uint8_t batteryLevel = *reinterpret_cast<const uint8_t *>(pBleServiceBattery->getValue(bleUuidCharacteristicBatteryLevel).data());
Log.straceln(FPSTR(L_SENSORS_BLE), "Battery: %d", batteryLevel);
} }
if (pBleServiceEnvironment) { // Reset init notify flag
float temperature = *reinterpret_cast<const int16_t *>(pBleServiceEnvironment->getValue(bleUuidCharacteristicTemperature).data()) / 100.0f; this->initBleNotify = false;
Log.straceln(FPSTR(L_SENSORS_BLE), "Temperature: %.2f", temperature);
float humidity = *reinterpret_cast<const int16_t *>(pBleServiceEnvironment->getValue(bleUuidCharacteristicHumidity).data()) / 100.0f;
Log.straceln(FPSTR(L_SENSORS_BLE), "Humidity: %.2f", humidity);
vars.temperatures.indoor = temperature + settings.sensors.indoor.offset; // Connect to the remote BLE Server.
BLEAddress bleServerAddress(settings.sensors.indoor.bleAddresss);
if (!pBleClient->connect(bleServerAddress)) {
Log.swarningln(FPSTR(L_SENSORS_BLE), "Failed connecting to device at %s", bleServerAddress.toString().c_str());
return;
} }
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Connected to device at %s", bleServerAddress.toString().c_str());
NimBLEUUID serviceUUID((uint16_t) 0x181AU);
BLERemoteService* pRemoteService = pBleClient->getService(serviceUUID);
if (!pRemoteService) {
Log.straceln(FPSTR(L_SENSORS_BLE), F("Failed to find service UUID: %s"), serviceUUID.toString().c_str());
return;
}
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found service UUID: %s"), serviceUUID.toString().c_str());
// 0x2A6E - Notify temperature x0.01C (pvvx)
if (!this->initBleNotify) {
NimBLEUUID charUUID((uint16_t) 0x2A6E);
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic && pRemoteCharacteristic->canNotify()) {
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found characteristic UUID: %s"), charUUID.toString().c_str());
this->initBleNotify = pRemoteCharacteristic->subscribe(true, [this](NimBLERemoteCharacteristic*, uint8_t* pData, size_t length, bool isNotify) {
if (length != 2) {
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Invalid notification data"));
return;
}
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.01);
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
if (this->emptyIndoorTemp) {
this->filteredIndoorTemp = rawTemp;
this->emptyIndoorTemp = false;
} else { } else {
Log.straceln(FPSTR(L_SENSORS_BLE), "Not connected"); this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
}
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
});
if (this->initBleNotify) {
Log.straceln(FPSTR(L_SENSORS_BLE), F("Subscribed to characteristic UUID: %s"), charUUID.toString().c_str());
} else {
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Failed to subscribe to characteristic UUID: %s"), charUUID.toString().c_str());
}
}
}
// 0x2A1F - Notify temperature x0.1C (atc1441/pvvx)
if (!this->initBleNotify) {
NimBLEUUID charUUID((uint16_t) 0x2A1F);
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic && pRemoteCharacteristic->canNotify()) {
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found characteristic UUID: %s"), charUUID.toString().c_str());
this->initBleNotify = pRemoteCharacteristic->subscribe(true, [this](NimBLERemoteCharacteristic*, uint8_t* pData, size_t length, bool isNotify) {
if (length != 2) {
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Invalid notification data"));
return;
}
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.1);
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
if (this->emptyIndoorTemp) {
this->filteredIndoorTemp = rawTemp;
this->emptyIndoorTemp = false;
} else {
this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
}
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
});
if (this->initBleNotify) {
Log.straceln(FPSTR(L_SENSORS_BLE), F("Subscribed to characteristic UUID: %s"), charUUID.toString().c_str());
} else {
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Failed to subscribe to characteristic UUID: %s"), charUUID.toString().c_str());
}
}
}
if (!this->initBleNotify) {
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Not found supported characteristics"));
pBleClient->disconnect();
} }
} }
#endif #endif
@@ -205,12 +269,6 @@ protected:
} }
this->filteredOutdoorTemp = floor(this->filteredOutdoorTemp * 100) / 100; this->filteredOutdoorTemp = floor(this->filteredOutdoorTemp * 100) / 100;
if (fabs(vars.temperatures.outdoor - this->filteredOutdoorTemp) > 0.099) {
vars.temperatures.outdoor = this->filteredOutdoorTemp + settings.sensors.outdoor.offset;
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("New temp: %f"), this->filteredOutdoorTemp);
}
this->outdoorSensor->requestTemperatures(); this->outdoorSensor->requestTemperatures();
this->startOutdoorConversionTime = millis(); this->startOutdoorConversionTime = millis();
} }
@@ -277,12 +335,6 @@ protected:
} }
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100; this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
if (fabs(vars.temperatures.indoor - this->filteredIndoorTemp) > 0.099) {
vars.temperatures.indoor = this->filteredIndoorTemp + settings.sensors.indoor.offset;
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("New temp: %f"), this->filteredIndoorTemp);
}
this->indoorSensor->requestTemperatures(); this->indoorSensor->requestTemperatures();
this->startIndoorConversionTime = millis(); this->startIndoorConversionTime = millis();
} }

View File

@@ -110,7 +110,7 @@ struct Settings {
// 1 - manual, 2 - ds18b20, 3 - ble // 1 - manual, 2 - ds18b20, 3 - ble
byte type = 1; byte type = 1;
byte pin = SENSOR_INDOOR_PIN_DEFAULT; byte pin = SENSOR_INDOOR_PIN_DEFAULT;
char bleAddresss[18] = "00:00:00:00:00:00"; uint8_t bleAddresss[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
float offset = 0.0f; float offset = 0.0f;
} indoor; } indoor;
} sensors; } sensors;
@@ -166,17 +166,17 @@ struct Variables {
unsigned long extPumpLastEnableTime = 0; unsigned long extPumpLastEnableTime = 0;
byte dhwMinTemp = DEFAULT_DHW_MIN_TEMP; byte dhwMinTemp = DEFAULT_DHW_MIN_TEMP;
byte dhwMaxTemp = DEFAULT_DHW_MAX_TEMP; byte dhwMaxTemp = DEFAULT_DHW_MAX_TEMP;
byte maxModulation; byte maxModulation = 0;
uint8_t slaveMemberId; uint8_t slaveMemberId = 0;
uint8_t slaveFlags; uint8_t slaveFlags = 0;
uint8_t slaveType; uint8_t slaveType = 0;
uint8_t slaveVersion; uint8_t slaveVersion = 0;
float slaveOtVersion; float slaveOtVersion = 0.0f;
uint8_t masterMemberId; uint8_t masterMemberId = 0;
uint8_t masterFlags; uint8_t masterFlags = 0;
uint8_t masterType; uint8_t masterType = 0;
uint8_t masterVersion; uint8_t masterVersion = 0;
float masterOtVersion; float masterOtVersion = 0;
} parameters; } parameters;
struct { struct {

View File

@@ -1,5 +1,5 @@
#define PROJECT_NAME "OpenTherm Gateway" #define PROJECT_NAME "OpenTherm Gateway"
#define PROJECT_VERSION "1.4.0-rc.14" #define PROJECT_VERSION "1.4.0-rc.16"
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway" #define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
#define EMERGENCY_TIME_TRESHOLD 120000 #define EMERGENCY_TIME_TRESHOLD 120000

View File

@@ -232,7 +232,7 @@ bool jsonToNetworkSettings(const JsonVariantConst src, NetworkSettings& dst) {
} }
// ap // sta
if (!src["sta"]["ssid"].isNull()) { if (!src["sta"]["ssid"].isNull()) {
String value = src["sta"]["ssid"].as<String>(); String value = src["sta"]["ssid"].as<String>();
@@ -329,7 +329,19 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
dst["sensors"]["indoor"]["type"] = src.sensors.indoor.type; dst["sensors"]["indoor"]["type"] = src.sensors.indoor.type;
dst["sensors"]["indoor"]["pin"] = src.sensors.indoor.pin; dst["sensors"]["indoor"]["pin"] = src.sensors.indoor.pin;
dst["sensors"]["indoor"]["bleAddresss"] = src.sensors.indoor.bleAddresss;
char bleAddress[18];
sprintf(
bleAddress,
"%02x:%02x:%02x:%02x:%02x:%02x",
src.sensors.indoor.bleAddresss[0],
src.sensors.indoor.bleAddresss[1],
src.sensors.indoor.bleAddresss[2],
src.sensors.indoor.bleAddresss[3],
src.sensors.indoor.bleAddresss[4],
src.sensors.indoor.bleAddresss[5]
);
dst["sensors"]["indoor"]["bleAddresss"] = String(bleAddress);
dst["sensors"]["indoor"]["offset"] = roundd(src.sensors.indoor.offset, 2); dst["sensors"]["indoor"]["offset"] = roundd(src.sensors.indoor.offset, 2);
if (!safe) { if (!safe) {
@@ -486,7 +498,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
if (!src["mqtt"]["port"].isNull()) { if (!src["mqtt"]["port"].isNull()) {
unsigned short value = src["mqtt"]["port"].as<unsigned short>(); unsigned short value = src["mqtt"]["port"].as<unsigned short>();
if (value >= 0 && value <= 65536) { if (value > 0 && value <= 65535) {
dst.mqtt.port = value; dst.mqtt.port = value;
changed = true; changed = true;
} }
@@ -821,9 +833,12 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
#if USE_BLE #if USE_BLE
if (!src["sensors"]["indoor"]["bleAddresss"].isNull()) { if (!src["sensors"]["indoor"]["bleAddresss"].isNull()) {
String value = src["sensors"]["indoor"]["bleAddresss"].as<String>(); String value = src["sensors"]["indoor"]["bleAddresss"].as<String>();
int tmp[6];
if(sscanf(value.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]) == 6) {
for(uint8_t i = 0; i < 6; i++) {
dst.sensors.indoor.bleAddresss[i] = (uint8_t) tmp[i];
}
if (value.length() < sizeof(dst.sensors.indoor.bleAddresss)) {
strcpy(dst.sensors.indoor.bleAddresss, value.c_str());
changed = true; changed = true;
} }
} }