diff --git a/README.md b/README.md index 4edf667..4225a2a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![logo](/assets/logo.svg)
- [![GitHub version](https://img.shields.io/github/release/Laxilef/OTGateway.svg)](https://github.com/Laxilef/OTGateway/releases) + [![GitHub version](https://img.shields.io/github/release/Laxilef/OTGateway.svg?include_prereleases)](https://github.com/Laxilef/OTGateway/releases) [![GitHub download](https://img.shields.io/github/downloads/Laxilef/OTGateway/total.svg)](https://github.com/Laxilef/OTGateway/releases/latest) [![License](https://img.shields.io/github/license/Laxilef/OTGateway.svg)](LICENSE.txt) [![Telegram](https://img.shields.io/badge/Telegram-Channel-33A8E3)](https://t.me/otgateway) diff --git a/platformio.ini b/platformio.ini index d24bd9c..3f6b841 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,9 +15,9 @@ extra_configs = secrets.default.ini [env] framework = arduino lib_deps = - bblanchon/ArduinoJson@^7.0.3 + bblanchon/ArduinoJson@^7.0.4 ;ihormelnyk/OpenTherm Library@^1.1.5 - https://github.com/ihormelnyk/opentherm_library.git + https://github.com/Laxilef/opentherm_library/archive/refs/heads/fix_lambda.zip arduino-libraries/ArduinoMqttClient@^0.1.8 lennarthennigs/ESP Telnet@^2.2 gyverlibs/FileData@^1.0.2 @@ -30,7 +30,7 @@ build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305 -mtext-section-literals -D MQTT_CLIENT_STD_FUNCTION_CALLBACK=1 - ;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_PORT=Serial + ;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_HTTP_SERVER -D DEBUG_ESP_PORT=Serial -D USE_SERIAL=${secrets.use_serial} -D USE_TELNET=${secrets.use_telnet} -D DEBUG_BY_DEFAULT=${secrets.debug} @@ -51,20 +51,19 @@ monitor_speed = 115200 monitor_filters = direct board_build.flash_mode = dio board_build.filesystem = littlefs -version = 1.4.0-rc.17 +version = 1.4.0-rc.18 ; Defaults [esp8266_defaults] platform = espressif8266 lib_deps = ${env.lib_deps} - nrwiersma/ESP8266Scheduler@^1.1 + nrwiersma/ESP8266Scheduler@^1.2 lib_ignore = extra_scripts = post:tools/build.py build_flags = ${env.build_flags} -board_build.ldscript = eagle.flash.1m256.ld -;board_build.ldscript = eagle.flash.4m1m.ld +board_build.ldscript = eagle.flash.4m1m.ld [esp32_defaults] platform = espressif32@^6.5 diff --git a/src/HaHelper.h b/src/HaHelper.h index 06f4d97..256653e 100644 --- a/src/HaHelper.h +++ b/src/HaHelper.h @@ -1276,6 +1276,36 @@ public: return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_temp")).c_str(), doc); } + bool publishSensorHeatingReturnTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) { + JsonDocument doc; + doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); + doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); + doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}"); + doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); + doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; + doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_return_temp")); + doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_return_temp")); + doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); + doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); + + if (unit == UnitSystem::METRIC) { + doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); + + } else if (unit == UnitSystem::IMPERIAL) { + doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F); + } + + doc[FPSTR(HA_NAME)] = F("Heating return temperature"); + doc[FPSTR(HA_ICON)] = F("mdi:radiator"); + doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); + doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.heatingReturn|float(0)|round(2) }}"); + doc[FPSTR(HA_EXPIRE_AFTER)] = 120; + doc.shrinkToFit(); + + return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_return_temp")).c_str(), doc); + } + bool publishSensorDhwTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) { JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); @@ -1306,6 +1336,36 @@ public: return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("dhw_temp")).c_str(), doc); } + bool publishSensorExhaustTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) { + JsonDocument doc; + doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); + doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state")); + doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}"); + doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); + doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; + doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("exhaust_temp")); + doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("exhaust_temp")); + doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic"); + doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_STATE_CLASS)] = F("measurement"); + + if (unit == UnitSystem::METRIC) { + doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); + + } else if (unit == UnitSystem::IMPERIAL) { + doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F); + } + + doc[FPSTR(HA_NAME)] = F("Exhaust temperature"); + doc[FPSTR(HA_ICON)] = F("mdi:smoke"); + doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state")); + doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.exhaust|float(0)|round(2) }}"); + doc[FPSTR(HA_EXPIRE_AFTER)] = 120; + doc.shrinkToFit(); + + return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("exhaust_temp")).c_str(), doc); + } + bool publishClimateHeating(byte minTemp = 20, byte maxTemp = 90, byte currentTempSource = HaHelper::TEMP_SOURCE_HEATING, bool enabledByDefault = true) { JsonDocument doc; diff --git a/src/MqttTask.h b/src/MqttTask.h index c7d4531..d22685b 100644 --- a/src/MqttTask.h +++ b/src/MqttTask.h @@ -370,6 +370,8 @@ protected: // temperatures this->haHelper->publishNumberIndoorTemp(); this->haHelper->publishSensorHeatingTemp(); + this->haHelper->publishSensorHeatingReturnTemp(settings.system.unitSystem, false); + this->haHelper->publishSensorExhaustTemp(settings.system.unitSystem, false); // buttons this->haHelper->publishButtonRestart(false); diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index 573f99b..4bb0cd8 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -253,9 +253,6 @@ protected: fsSettings.update(); } - // Force set max heating temp - setMaxHeatingTemp(settings.heating.maxTemp); - // Get outdoor temp (if necessary) if (settings.sensors.outdoor.type == SensorType::BOILER) { updateOutsideTemp(); @@ -293,6 +290,12 @@ protected: // Get current heating temp updateHeatingTemp(); + // Get heating return temp + updateHeatingReturnTemp(); + + // Get exhaust temp + updateExhaustTemp(); + // Fault reset action if (vars.actions.resetFault) { @@ -355,7 +358,7 @@ protected: Log.sinfoln(FPSTR(L_OT_HEATING), F("Set temp = %u"), vars.parameters.heatingSetpoint); // Set heating temp - if (this->instance->setHeatingCh1Temp(tempTo(vars.parameters.heatingSetpoint))) { + if (this->instance->setHeatingCh1Temp(tempTo(vars.parameters.heatingSetpoint)) || this->setMaxHeatingTemp(tempTo(vars.parameters.heatingSetpoint))) { currentHeatingTemp = vars.parameters.heatingSetpoint; this->heatingSetTempTime = millis(); @@ -657,6 +660,21 @@ protected: return true; } + bool updateExhaustTemp() { + unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::READ_DATA, + OpenThermMessageID::Texhaust, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + } + + vars.temperatures.exhaust = tempFrom(CustomOpenTherm::getFloat(response)); + return true; + } + bool updateHeatingTemp() { unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( OpenThermMessageType::READ_DATA, @@ -677,6 +695,21 @@ protected: return true; } + bool updateHeatingReturnTemp() { + unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermMessageType::READ_DATA, + OpenThermMessageID::Tret, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + } + + vars.temperatures.heatingReturn = tempFrom(CustomOpenTherm::getFloat(response)); + return true; + } + bool updateDhwTemp() { unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( diff --git a/src/Settings.h b/src/Settings.h index 32954e5..c9c50ea 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -155,7 +155,9 @@ struct Variables { float indoor = 0.0f; float outdoor = 0.0f; float heating = 0.0f; + float heatingReturn = 0.0f; float dhw = 0.0f; + float exhaust = 0.0f; } temperatures; struct { diff --git a/src/defines.h b/src/defines.h index 420dc47..8ca710f 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,5 +1,5 @@ #define PROJECT_NAME "OpenTherm Gateway" -#define PROJECT_VERSION "1.4.0-rc.17" +#define PROJECT_VERSION "1.4.0-rc.18" #define PROJECT_REPO "https://github.com/Laxilef/OTGateway" #define EMERGENCY_TIME_TRESHOLD 120000 diff --git a/src/utils.h b/src/utils.h index c1c584f..1fce972 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1058,7 +1058,9 @@ void varsToJson(const Variables& src, JsonVariant dst) { dst["temperatures"]["indoor"] = roundd(src.temperatures.indoor, 2); dst["temperatures"]["outdoor"] = roundd(src.temperatures.outdoor, 2); dst["temperatures"]["heating"] = roundd(src.temperatures.heating, 2); + dst["temperatures"]["heatingReturn"] = roundd(src.temperatures.heatingReturn, 2); dst["temperatures"]["dhw"] = roundd(src.temperatures.dhw, 2); + dst["temperatures"]["exhaust"] = roundd(src.temperatures.exhaust, 2); dst["parameters"]["heatingEnabled"] = src.parameters.heatingEnabled; dst["parameters"]["heatingMinTemp"] = src.parameters.heatingMinTemp;