mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 18:54:28 +05:00
Compare commits
17 Commits
1.4.0-rc.1
...
1.4.0-rc.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cbc52a8b0 | ||
|
|
e090be380c | ||
|
|
0bf49d2249 | ||
|
|
a83d94d361 | ||
|
|
358980da4c | ||
|
|
f91e39d067 | ||
|
|
1d53f21d46 | ||
|
|
c225e7c2a8 | ||
|
|
6831c4331f | ||
|
|
8fb62ce8ae | ||
|
|
e829a00355 | ||
|
|
bee720386a | ||
|
|
c4b6eadb81 | ||
|
|
a5d2b9fcfa | ||
|
|
1a03117257 | ||
|
|
b421780f7b | ||
|
|
987c101394 |
@@ -16,7 +16,7 @@
|
||||
- PID
|
||||
- Equithermic curves - adjusts the temperature based on indoor and outdoor temperatures
|
||||
- Hysteresis setting (for accurate maintenance of room temperature)
|
||||
- Ability to connect an external sensors to monitor outdoor and indoor temperature ([compatible sensors](#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).
|
||||
- Automatic error reset (not with all boilers)
|
||||
- Diagnostics:
|
||||
@@ -72,7 +72,7 @@ All available information and instructions can be found in the wiki:
|
||||
- [ESP32Scheduler](https://github.com/laxilef/ESP32Scheduler) (for ESP32)
|
||||
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
|
||||
- [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library)
|
||||
- [ArduinoMqttClient](https://github.com/arduino-libraries/ArduinoMqttClient) /
|
||||
- [ArduinoMqttClient](https://github.com/arduino-libraries/ArduinoMqttClient)
|
||||
- [ESPTelnet](https://github.com/LennartHennigs/ESPTelnet)
|
||||
- [FileData](https://github.com/GyverLibs/FileData)
|
||||
- [GyverPID](https://github.com/GyverLibs/GyverPID)
|
||||
|
||||
@@ -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/" 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/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>
|
||||
</small>
|
||||
</footer>
|
||||
@@ -227,4 +227,4 @@
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -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/" 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/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>
|
||||
</small>
|
||||
</footer>
|
||||
@@ -163,4 +163,4 @@
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -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/" 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/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>
|
||||
</small>
|
||||
</footer>
|
||||
@@ -354,4 +354,4 @@
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -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/" 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/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>
|
||||
</small>
|
||||
</footer>
|
||||
@@ -105,4 +105,4 @@
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
float Kk = 0.0;
|
||||
float Kt = 0.0;
|
||||
|
||||
Equitherm() {}
|
||||
Equitherm() = default;
|
||||
|
||||
// kn, kk, kt
|
||||
Equitherm(float new_kn, float new_kk, float new_kt) {
|
||||
|
||||
@@ -6,7 +6,7 @@ class HomeAssistantHelper {
|
||||
public:
|
||||
typedef std::function<void(const char*, bool)> PublishEventCallback;
|
||||
|
||||
HomeAssistantHelper() {}
|
||||
HomeAssistantHelper() = default;
|
||||
|
||||
void setWriter() {
|
||||
this->writer = nullptr;
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace Network {
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
WiFi.setSleepMode(WIFI_NONE_SLEEP);
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
WiFi.setSleep(WIFI_PS_NONE);
|
||||
WiFi.setSleep(USE_BLE ? WIFI_PS_MIN_MODEM : WIFI_PS_NONE);
|
||||
#endif
|
||||
|
||||
WiFi.softAPdisconnect();
|
||||
@@ -309,12 +309,14 @@ namespace Network {
|
||||
Log.sinfoln(FPSTR(L_NETWORK), F("No STA credentials, start AP"));
|
||||
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
this->delayCallback(250);
|
||||
WiFi.softAP(this->apName, this->apPassword, this->apChannel);
|
||||
|
||||
} else if (!this->isApEnabled() && millis() - this->disconnectedTime > this->failedConnectTimeout) {
|
||||
Log.sinfoln(FPSTR(L_NETWORK), F("Disconnected for a long time, start AP"));
|
||||
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
this->delayCallback(250);
|
||||
WiFi.softAP(this->apName, this->apPassword, this->apChannel);
|
||||
|
||||
} else if (this->isConnecting() && millis() - this->prevReconnectingTime > this->resetConnectionTimeout) {
|
||||
|
||||
@@ -177,7 +177,6 @@ public:
|
||||
|
||||
} else {
|
||||
sizeArgName = length - size_t(argStartPos - currentBuf) - 1;
|
||||
Serial.printf("sizeArgName: %d\r\n", sizeArgName);
|
||||
|
||||
// send all content if arg len > space
|
||||
if (sizeArgName >= sizeof(argName)) {
|
||||
|
||||
@@ -15,7 +15,7 @@ extra_configs = secrets.default.ini
|
||||
[env]
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson@^7.0.2
|
||||
bblanchon/ArduinoJson@^7.0.3
|
||||
;ihormelnyk/OpenTherm Library@^1.1.4
|
||||
https://github.com/Laxilef/opentherm_library/archive/refs/heads/fix_start_bit.zip
|
||||
arduino-libraries/ArduinoMqttClient@^0.1.8
|
||||
@@ -51,7 +51,7 @@ monitor_speed = 115200
|
||||
monitor_filters = direct
|
||||
board_build.flash_mode = dio
|
||||
board_build.filesystem = littlefs
|
||||
version = 1.4.0-rc.13
|
||||
version = 1.4.0-rc.16
|
||||
|
||||
; Defaults
|
||||
[esp8266_defaults]
|
||||
@@ -65,8 +65,6 @@ extra_scripts =
|
||||
build_flags = ${env.build_flags}
|
||||
board_build.ldscript = eagle.flash.1m256.ld
|
||||
;board_build.ldscript = eagle.flash.4m1m.ld
|
||||
platform_packages =
|
||||
framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git
|
||||
|
||||
[esp32_defaults]
|
||||
platform = espressif32
|
||||
|
||||
139
src/HaHelper.h
139
src/HaHelper.h
@@ -6,59 +6,6 @@ public:
|
||||
static const byte TEMP_SOURCE_HEATING = 0;
|
||||
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) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
@@ -266,7 +213,10 @@ public:
|
||||
|
||||
bool publishSensorBoilerHeatingMinTemp(bool enabledByDefault = true) {
|
||||
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_UNIQUE_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) {
|
||||
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_UNIQUE_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_TEMPLATE)] = F("{\"dhw\": {\"target\" : {{ value|int(0) }}}}");
|
||||
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_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -426,7 +379,10 @@ public:
|
||||
|
||||
bool publishSensorBoilerDhwMinTemp(bool enabledByDefault = true) {
|
||||
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_UNIQUE_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) {
|
||||
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_UNIQUE_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) {
|
||||
JsonDocument doc;
|
||||
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_TEMPLATE)] = F("{\"tuning\": {\"regulator\": {% if value == 'Equitherm' %}0{% elif value == 'PID' %}1{% endif %}}}");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
@@ -835,7 +793,10 @@ public:
|
||||
|
||||
bool publishBinSensorHeating(bool enabledByDefault = true) {
|
||||
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_UNIQUE_ID)] = this->getObjectId(F("heating"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating"));
|
||||
@@ -853,7 +814,10 @@ public:
|
||||
|
||||
bool publishBinSensorDhw(bool enabledByDefault = true) {
|
||||
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_UNIQUE_ID)] = this->getObjectId(F("dhw"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw"));
|
||||
@@ -871,7 +835,10 @@ public:
|
||||
|
||||
bool publishBinSensorFlame(bool enabledByDefault = true) {
|
||||
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_UNIQUE_ID)] = this->getObjectId(F("flame"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("flame"));
|
||||
@@ -889,8 +856,10 @@ public:
|
||||
|
||||
bool publishBinSensorFault(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("fault"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("fault"));
|
||||
@@ -908,7 +877,10 @@ public:
|
||||
|
||||
bool publishBinSensorDiagnostic(bool enabledByDefault = true) {
|
||||
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_UNIQUE_ID)] = this->getObjectId(F("diagnostic"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("diagnostic"));
|
||||
@@ -926,8 +898,10 @@ public:
|
||||
|
||||
bool publishSensorFaultCode(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.fault, 'online', 'offline') }}");
|
||||
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 and value_json.states.fault, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_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) {
|
||||
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_UNIQUE_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) {
|
||||
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_UNIQUE_ID)] = this->getObjectId(F("pressure"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("pressure"));
|
||||
@@ -1025,7 +1005,10 @@ public:
|
||||
|
||||
bool publishSensorDhwFlowRate(bool enabledByDefault = true) {
|
||||
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_UNIQUE_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) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("any");
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_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) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("any");
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_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) {
|
||||
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_UNIQUE_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) {
|
||||
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_UNIQUE_ID)] = this->getObjectId(F("dhw_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_temp"));
|
||||
|
||||
@@ -20,14 +20,11 @@ public:
|
||||
}
|
||||
|
||||
~MainTask() {
|
||||
if (this->blinker != nullptr) {
|
||||
delete this->blinker;
|
||||
}
|
||||
delete this->blinker;
|
||||
}
|
||||
|
||||
protected:
|
||||
const static byte REASON_PUMP_START_HEATING = 1;
|
||||
const static byte REASON_PUMP_START_ANTISTUCK = 2;
|
||||
enum class PumpStartReason {NONE, HEATING, ANTISTUCK};
|
||||
|
||||
Blinker* blinker = nullptr;
|
||||
bool blinkerInitialized = false;
|
||||
@@ -38,7 +35,7 @@ protected:
|
||||
unsigned long restartSignalTime = 0;
|
||||
bool heatingEnabled = false;
|
||||
unsigned long heatingDisabledTime = 0;
|
||||
byte externalPumpStartReason;
|
||||
PumpStartReason extPumpStartReason = PumpStartReason::NONE;
|
||||
unsigned long externalPumpStartTime = 0;
|
||||
bool telnetStarted = false;
|
||||
|
||||
@@ -57,12 +54,12 @@ protected:
|
||||
void setup() {
|
||||
#ifdef LED_STATUS_PIN
|
||||
pinMode(LED_STATUS_PIN, OUTPUT);
|
||||
digitalWrite(LED_STATUS_PIN, false);
|
||||
digitalWrite(LED_STATUS_PIN, LOW);
|
||||
#endif
|
||||
|
||||
if (settings.externalPump.pin != 0) {
|
||||
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 (errCount == 0) {
|
||||
if (!ledOn) {
|
||||
digitalWrite(ledPin, true);
|
||||
digitalWrite(ledPin, HIGH);
|
||||
ledOn = true;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
} else if (ledOn) {
|
||||
digitalWrite(ledPin, false);
|
||||
digitalWrite(ledPin, LOW);
|
||||
ledOn = false;
|
||||
endBlinkTime = millis();
|
||||
return;
|
||||
@@ -289,7 +286,7 @@ protected:
|
||||
if (!settings.externalPump.use || settings.externalPump.pin == 0) {
|
||||
if (vars.states.externalPump) {
|
||||
if (settings.externalPump.pin != 0) {
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
digitalWrite(settings.externalPump.pin, LOW);
|
||||
}
|
||||
|
||||
vars.states.externalPump = false;
|
||||
@@ -302,16 +299,16 @@ protected:
|
||||
}
|
||||
|
||||
if (vars.states.externalPump && !this->heatingEnabled) {
|
||||
if (this->externalPumpStartReason == MainTask::REASON_PUMP_START_HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) {
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
if (this->extPumpStartReason == MainTask::PumpStartReason::HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) {
|
||||
digitalWrite(settings.externalPump.pin, LOW);
|
||||
|
||||
vars.states.externalPump = false;
|
||||
vars.parameters.extPumpLastEnableTime = millis();
|
||||
|
||||
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)) {
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
} else if (this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK && millis() - this->externalPumpStartTime >= (settings.externalPump.antiStuckTime * 1000u)) {
|
||||
digitalWrite(settings.externalPump.pin, LOW);
|
||||
|
||||
vars.states.externalPump = false;
|
||||
vars.parameters.extPumpLastEnableTime = millis();
|
||||
@@ -319,24 +316,24 @@ protected:
|
||||
Log.sinfoln("EXTPUMP", F("Disabled: expired anti stuck time"));
|
||||
}
|
||||
|
||||
} else if (vars.states.externalPump && this->heatingEnabled && this->externalPumpStartReason == MainTask::REASON_PUMP_START_ANTISTUCK) {
|
||||
this->externalPumpStartReason = MainTask::REASON_PUMP_START_HEATING;
|
||||
} else if (vars.states.externalPump && this->heatingEnabled && this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK) {
|
||||
this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
|
||||
|
||||
} else if (!vars.states.externalPump && this->heatingEnabled) {
|
||||
vars.states.externalPump = true;
|
||||
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"));
|
||||
|
||||
} else if (!vars.states.externalPump && (vars.parameters.extPumpLastEnableTime == 0 || millis() - vars.parameters.extPumpLastEnableTime >= (settings.externalPump.antiStuckInterval * 1000ul))) {
|
||||
vars.states.externalPump = true;
|
||||
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"));
|
||||
}
|
||||
|
||||
@@ -15,9 +15,7 @@ public:
|
||||
}
|
||||
|
||||
~MqttTask() {
|
||||
if (this->haHelper != nullptr) {
|
||||
delete this->haHelper;
|
||||
}
|
||||
delete this->haHelper;
|
||||
|
||||
if (this->client != nullptr) {
|
||||
if (this->client->connected()) {
|
||||
@@ -27,13 +25,8 @@ public:
|
||||
delete this->client;
|
||||
}
|
||||
|
||||
if (this->writer != nullptr) {
|
||||
delete this->writer;
|
||||
}
|
||||
|
||||
if (this->wifiClient != nullptr) {
|
||||
delete this->wifiClient;
|
||||
}
|
||||
delete this->writer;
|
||||
delete this->wifiClient;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
@@ -208,12 +201,7 @@ protected:
|
||||
|
||||
// publish variables and status
|
||||
if (this->newConnection || millis() - this->prevPubVarsTime > (settings.mqtt.interval * 1000u)) {
|
||||
this->writer->publish(
|
||||
this->haHelper->getDeviceTopic("status").c_str(),
|
||||
!vars.states.otStatus ? "offline" : vars.states.fault ? "fault" : "online",
|
||||
true
|
||||
);
|
||||
|
||||
this->writer->publish(this->haHelper->getDeviceTopic("status").c_str(), "online", false);
|
||||
this->publishVariables(this->haHelper->getDeviceTopic("state").c_str());
|
||||
this->prevPubVarsTime = millis();
|
||||
}
|
||||
@@ -328,10 +316,6 @@ protected:
|
||||
}
|
||||
|
||||
void publishHaEntities() {
|
||||
// main
|
||||
this->haHelper->publishNumberOutdoorSensorOffset(false);
|
||||
this->haHelper->publishNumberIndoorSensorOffset(false);
|
||||
|
||||
// emergency
|
||||
this->haHelper->publishSwitchEmergency();
|
||||
this->haHelper->publishNumberEmergencyTarget();
|
||||
|
||||
@@ -54,9 +54,9 @@ protected:
|
||||
|
||||
#ifdef LED_OT_RX_PIN
|
||||
{
|
||||
digitalWrite(LED_OT_RX_PIN, true);
|
||||
digitalWrite(LED_OT_RX_PIN, HIGH);
|
||||
delayMicroseconds(2000);
|
||||
digitalWrite(LED_OT_RX_PIN, false);
|
||||
digitalWrite(LED_OT_RX_PIN, LOW);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -71,7 +71,7 @@ protected:
|
||||
|
||||
#ifdef LED_OT_RX_PIN
|
||||
pinMode(LED_OT_RX_PIN, OUTPUT);
|
||||
digitalWrite(LED_OT_RX_PIN, false);
|
||||
digitalWrite(LED_OT_RX_PIN, LOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,7 @@ public:
|
||||
}
|
||||
|
||||
~PortalTask() {
|
||||
if (this->bufferedWebServer != nullptr) {
|
||||
delete this->bufferedWebServer;
|
||||
}
|
||||
delete this->bufferedWebServer;
|
||||
|
||||
if (this->webServer != nullptr) {
|
||||
this->stopWebServer();
|
||||
|
||||
@@ -159,7 +159,7 @@ protected:
|
||||
if (vars.parameters.heatingEnabled) {
|
||||
float pidResult = getPidTemp(
|
||||
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) {
|
||||
|
||||
@@ -3,13 +3,6 @@
|
||||
|
||||
#if USE_BLE
|
||||
#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
|
||||
|
||||
class SensorsTask : public LeanTask {
|
||||
@@ -25,21 +18,10 @@ public:
|
||||
}
|
||||
|
||||
~SensorsTask() {
|
||||
if (this->outdoorSensor != nullptr) {
|
||||
delete this->outdoorSensor;
|
||||
}
|
||||
|
||||
if (this->oneWireOutdoorSensor != nullptr) {
|
||||
delete this->oneWireOutdoorSensor;
|
||||
}
|
||||
|
||||
if (this->indoorSensor != nullptr) {
|
||||
delete this->indoorSensor;
|
||||
}
|
||||
|
||||
if (this->oneWireIndoorSensor != nullptr) {
|
||||
delete this->oneWireIndoorSensor;
|
||||
}
|
||||
delete this->outdoorSensor;
|
||||
delete this->oneWireOutdoorSensor;
|
||||
delete this->indoorSensor;
|
||||
delete this->oneWireIndoorSensor;
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -61,9 +43,8 @@ protected:
|
||||
|
||||
#if USE_BLE
|
||||
BLEClient* pBleClient = nullptr;
|
||||
BLERemoteService* pBleServiceBattery = nullptr;
|
||||
BLERemoteService* pBleServiceEnvironment = nullptr;
|
||||
bool initBleSensor = false;
|
||||
bool initBleNotify = false;
|
||||
#endif
|
||||
|
||||
const char* getTaskName() {
|
||||
@@ -79,67 +60,150 @@ protected:
|
||||
}
|
||||
|
||||
void loop() {
|
||||
bool needUpdateIndoorTemp = false;
|
||||
bool needUpdateOutdoorTemp = false;
|
||||
|
||||
if (settings.sensors.outdoor.type == 2 && settings.sensors.outdoor.pin) {
|
||||
outdoorTemperatureSensor();
|
||||
needUpdateOutdoorTemp = true;
|
||||
}
|
||||
|
||||
if (settings.sensors.indoor.type == 2 && settings.sensors.indoor.pin) {
|
||||
indoorTemperatureSensor();
|
||||
needUpdateIndoorTemp = true;
|
||||
}
|
||||
|
||||
#if USE_BLE
|
||||
if (settings.sensors.indoor.type == 3 && strlen(settings.sensors.indoor.bleAddresss)) {
|
||||
if (settings.sensors.indoor.type == 3) {
|
||||
bluetoothSensor();
|
||||
needUpdateIndoorTemp = true;
|
||||
}
|
||||
#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
|
||||
void bluetoothSensor() {
|
||||
static bool initBleNotify = false;
|
||||
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("");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (pBleClient && pBleClient->isConnected()) {
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), "Connected. Free heap %u bytes", ESP.getFreeHeap());
|
||||
if (pBleServiceBattery) {
|
||||
uint8_t batteryLevel = *reinterpret_cast<const uint8_t *>(pBleServiceBattery->getValue(bleUuidCharacteristicBatteryLevel).data());
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), "Battery: %d", batteryLevel);
|
||||
}
|
||||
if (!initBleSensor || pBleClient->isConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset init notify flag
|
||||
this->initBleNotify = false;
|
||||
|
||||
if (pBleServiceEnvironment) {
|
||||
float temperature = *reinterpret_cast<const int16_t *>(pBleServiceEnvironment->getValue(bleUuidCharacteristicTemperature).data()) / 100.0f;
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
|
||||
vars.temperatures.indoor = temperature + settings.sensors.indoor.offset;
|
||||
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 {
|
||||
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());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), "Not connected");
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -205,12 +269,6 @@ protected:
|
||||
}
|
||||
|
||||
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->startOutdoorConversionTime = millis();
|
||||
}
|
||||
@@ -277,12 +335,6 @@ protected:
|
||||
}
|
||||
|
||||
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->startIndoorConversionTime = millis();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ struct NetworkSettings {
|
||||
struct {
|
||||
char ssid[33] = AP_SSID_DEFAULT;
|
||||
char password[65] = AP_PASSWORD_DEFAULT;
|
||||
byte channel = 1;
|
||||
byte channel = 6;
|
||||
} ap;
|
||||
|
||||
struct {
|
||||
@@ -110,7 +110,7 @@ struct Settings {
|
||||
// 1 - manual, 2 - ds18b20, 3 - ble
|
||||
byte type = 1;
|
||||
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;
|
||||
} indoor;
|
||||
} sensors;
|
||||
@@ -166,17 +166,17 @@ struct Variables {
|
||||
unsigned long extPumpLastEnableTime = 0;
|
||||
byte dhwMinTemp = DEFAULT_DHW_MIN_TEMP;
|
||||
byte dhwMaxTemp = DEFAULT_DHW_MAX_TEMP;
|
||||
byte maxModulation;
|
||||
uint8_t slaveMemberId;
|
||||
uint8_t slaveFlags;
|
||||
uint8_t slaveType;
|
||||
uint8_t slaveVersion;
|
||||
float slaveOtVersion;
|
||||
uint8_t masterMemberId;
|
||||
uint8_t masterFlags;
|
||||
uint8_t masterType;
|
||||
uint8_t masterVersion;
|
||||
float masterOtVersion;
|
||||
byte maxModulation = 0;
|
||||
uint8_t slaveMemberId = 0;
|
||||
uint8_t slaveFlags = 0;
|
||||
uint8_t slaveType = 0;
|
||||
uint8_t slaveVersion = 0;
|
||||
float slaveOtVersion = 0.0f;
|
||||
uint8_t masterMemberId = 0;
|
||||
uint8_t masterFlags = 0;
|
||||
uint8_t masterType = 0;
|
||||
uint8_t masterVersion = 0;
|
||||
float masterOtVersion = 0;
|
||||
} parameters;
|
||||
|
||||
struct {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#define PROJECT_NAME "OpenTherm Gateway"
|
||||
#define PROJECT_VERSION "1.4.0-rc.13"
|
||||
#define PROJECT_VERSION "1.4.0-rc.16"
|
||||
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
|
||||
|
||||
#define EMERGENCY_TIME_TRESHOLD 120000
|
||||
|
||||
25
src/utils.h
25
src/utils.h
@@ -232,7 +232,7 @@ bool jsonToNetworkSettings(const JsonVariantConst src, NetworkSettings& dst) {
|
||||
}
|
||||
|
||||
|
||||
// ap
|
||||
// sta
|
||||
if (!src["sta"]["ssid"].isNull()) {
|
||||
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"]["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);
|
||||
|
||||
if (!safe) {
|
||||
@@ -486,7 +498,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
if (!src["mqtt"]["port"].isNull()) {
|
||||
unsigned short value = src["mqtt"]["port"].as<unsigned short>();
|
||||
|
||||
if (value >= 0 && value <= 65536) {
|
||||
if (value > 0 && value <= 65535) {
|
||||
dst.mqtt.port = value;
|
||||
changed = true;
|
||||
}
|
||||
@@ -821,9 +833,12 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
#if USE_BLE
|
||||
if (!src["sensors"]["indoor"]["bleAddresss"].isNull()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user