diff --git a/lib/CustomOpenTherm/CustomOpenTherm.h b/lib/CustomOpenTherm/CustomOpenTherm.h index 28db14d..389cfe5 100644 --- a/lib/CustomOpenTherm/CustomOpenTherm.h +++ b/lib/CustomOpenTherm/CustomOpenTherm.h @@ -7,7 +7,7 @@ public: typedef std::function BeforeSendRequestCallback; typedef std::function AfterSendRequestCallback; - CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false) : OpenTherm(inPin, outPin, isSlave) {} + CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false, bool alwaysReceive = false) : OpenTherm(inPin, outPin, isSlave, alwaysReceive) {} ~CustomOpenTherm() {} CustomOpenTherm* setDelayCallback(DelayCallback callback = nullptr) { @@ -28,8 +28,8 @@ public: return this; } - unsigned long sendRequest(unsigned long request, byte attempts = 5, byte _attempt = 0) { - _attempt++; + unsigned long sendRequest(unsigned long request) override { + this->sendRequestAttempt++; while (!this->isReady()) { if (this->delayCallback) { @@ -40,15 +40,10 @@ public: } if (this->beforeSendRequestCallback) { - this->beforeSendRequestCallback(request, _attempt); + this->beforeSendRequestCallback(request, this->sendRequestAttempt); } - unsigned long _response; - OpenThermResponseStatus _responseStatus = OpenThermResponseStatus::NONE; - if (!this->sendRequestAsync(request)) { - _response = 0; - - } else { + if (this->sendRequestAsync(request)) { do { if (this->delayCallback) { this->delayCallback(150); @@ -56,42 +51,25 @@ public: this->process(); } while (this->status != OpenThermStatus::READY && this->status != OpenThermStatus::DELAY); - - _response = this->getLastResponse(); - _responseStatus = this->getLastResponseStatus(); } if (this->afterSendRequestCallback) { - this->afterSendRequestCallback(request, _response, _responseStatus, _attempt); + this->afterSendRequestCallback(request, this->response, this->responseStatus, this->sendRequestAttempt); } - if (_responseStatus == OpenThermResponseStatus::SUCCESS || _responseStatus == OpenThermResponseStatus::INVALID || _attempt >= attempts) { - return _response; + if (this->responseStatus == OpenThermResponseStatus::SUCCESS || this->responseStatus == OpenThermResponseStatus::INVALID) { + this->sendRequestAttempt = 0; + return this->response; + + } else if (this->sendRequestAttempt >= this->sendRequestMaxAttempts) { + this->sendRequestAttempt = 0; + return this->response; } else { - return this->sendRequest(request, attempts, _attempt); + return this->sendRequest(request); } } - unsigned long setBoilerStatus(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2, bool summerWinterMode, bool dhwBlocking, uint8_t lb = 0) { - unsigned int data = enableCentralHeating - | (enableHotWater << 1) - | (enableCooling << 2) - | (enableOutsideTemperatureCompensation << 3) - | (enableCentralHeating2 << 4) - | (summerWinterMode << 5) - | (dhwBlocking << 6); - - data <<= 8; - data |= lb; - - return this->sendRequest(buildRequest( - OpenThermMessageType::READ_DATA, - OpenThermMessageID::Status, - data - )); - } - bool sendBoilerReset() { unsigned int data = 1; data <<= 8; @@ -130,10 +108,31 @@ public: static bool isValidResponseId(unsigned long response, OpenThermMessageID id) { byte responseId = (response >> 16) & 0xFF; - + return (byte)id == responseId; } + static uint8_t getResponseMessageTypeId(unsigned long response) { + return (response << 1) >> 29; + } + + static const char* getResponseMessageTypeString(unsigned long response) { + uint8_t msgType = getResponseMessageTypeId(response); + + switch (msgType) { + case (uint8_t) OpenThermMessageType::READ_ACK: + case (uint8_t) OpenThermMessageType::WRITE_ACK: + case (uint8_t) OpenThermMessageType::DATA_INVALID: + case (uint8_t) OpenThermMessageType::UNKNOWN_DATA_ID: + return CustomOpenTherm::messageTypeToString( + static_cast(msgType) + ); + + default: + return "UNKNOWN"; + } + } + // converters template static unsigned int toFloat(const T val) { @@ -145,6 +144,8 @@ public: } protected: + const uint8_t sendRequestMaxAttempts = 5; + uint8_t sendRequestAttempt = 0; DelayCallback delayCallback; BeforeSendRequestCallback beforeSendRequestCallback; AfterSendRequestCallback afterSendRequestCallback; diff --git a/platformio.ini b/platformio.ini index 5394ac3..f2e2727 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,12 +14,12 @@ extra_configs = secrets.default.ini core_dir = .pio [env] -version = 1.5.3 +version = 1.5.4 framework = arduino lib_deps = bblanchon/ArduinoJson@^7.3.0 ;ihormelnyk/OpenTherm Library@^1.1.5 - https://github.com/ihormelnyk/opentherm_library#master + https://github.com/Laxilef/opentherm_library#esp32_timer arduino-libraries/ArduinoMqttClient@^0.1.8 lennarthennigs/ESP Telnet@^2.2 gyverlibs/FileData@^1.0.2 @@ -84,7 +84,7 @@ board_build.ldscript = eagle.flash.4m1m.ld ;platform_packages = ; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.5 ; framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.1/esp32-arduino-libs-idf-release_v5.1-33fbade6.zip -platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.13/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20/platform-espressif32.zip platform_packages = board_build.partitions = esp32_partitions.csv lib_deps = diff --git a/src/HaHelper.h b/src/HaHelper.h index 156c2e0..9258ee3 100644 --- a/src/HaHelper.h +++ b/src/HaHelper.h @@ -395,7 +395,7 @@ public: doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); - doc[FPSTR(HA_DEVICE_CLASS)] = F("signal_strength"); + //doc[FPSTR(HA_DEVICE_CLASS)] = F("signal_strength"); doc[FPSTR(HA_STATE_CLASS)] = FPSTR(HA_STATE_CLASS_MEASUREMENT); doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT); doc[FPSTR(HA_ICON)] = F("mdi:signal"); diff --git a/src/MainTask.h b/src/MainTask.h index f581adb..65aba81 100644 --- a/src/MainTask.h +++ b/src/MainTask.h @@ -196,6 +196,7 @@ protected: // critical heap if (!vars.states.restarting && (freeHeap < 2048 || maxFreeBlockHeap < 2048)) { this->restartSignalReceivedTime = millis(); + this->restartSignalReceived = true; vars.states.restarting = true; } diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index b6a0e98..9b1d72a 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -93,8 +93,13 @@ protected: this->instance->setAfterSendRequestCallback([this](unsigned long request, unsigned long response, OpenThermResponseStatus status, byte attempt) { Log.sverboseln( FPSTR(L_OT), - F("ID: %4d Request: %8lx Response: %8lx Attempt: %2d Status: %s"), - CustomOpenTherm::getDataID(request), request, response, attempt, CustomOpenTherm::statusToString(status) + F("ID: %4d Request: %8lx Response: %8lx Msg type: %s Attempt: %2d Status: %s"), + CustomOpenTherm::getDataID(request), + request, + response, + CustomOpenTherm::getResponseMessageTypeString(response), + attempt, + CustomOpenTherm::statusToString(status) ); if (status == OpenThermResponseStatus::SUCCESS) { @@ -138,7 +143,12 @@ protected: return; } else if (this->instance->status == OpenThermStatus::NOT_INITIALIZED) { - this->instance->begin(); + if (!this->instance->begin()) { + Log.swarningln(FPSTR(L_OT), F("Failed begin")); + + this->delay(5000); + return; + } } // RX LED GPIO setup @@ -212,6 +222,26 @@ protected: F("Failed receive boiler status: %s"), CustomOpenTherm::statusToString(this->instance->getLastResponseStatus()) ); + + } else { + vars.slave.heating.active = CustomOpenTherm::isCentralHeatingActive(response); + vars.slave.dhw.active = settings.opentherm.options.dhwSupport ? CustomOpenTherm::isHotWaterActive(response) : false; + vars.slave.flame = CustomOpenTherm::isFlameOn(response); + vars.slave.cooling = CustomOpenTherm::isCoolingActive(response); + vars.slave.fault.active = CustomOpenTherm::isFault(response); + + if (!settings.opentherm.options.ignoreDiagState) { + vars.slave.diag.active = CustomOpenTherm::isDiagnostic(response); + + } else if (vars.slave.diag.active) { + vars.slave.diag.active = false; + } + + Log.snoticeln( + FPSTR(L_OT), F("Received boiler status. Heating: %hhu; DHW: %hhu; flame: %hhu; cooling: %hhu; fault: %hhu; diag: %hhu"), + vars.slave.heating.active, vars.slave.dhw.active, + vars.slave.flame, vars.slave.cooling, vars.slave.fault.active, vars.slave.diag.active + ); } // 5 request retries @@ -257,6 +287,15 @@ protected: Sensors::setConnectionStatusByType(Sensors::Type::OT_FAN_SPEED_SETPOINT, false); Sensors::setConnectionStatusByType(Sensors::Type::OT_FAN_SPEED_CURRENT, false); + Sensors::setConnectionStatusByType(Sensors::Type::OT_BURNER_STARTS, false); + Sensors::setConnectionStatusByType(Sensors::Type::OT_DHW_BURNER_STARTS, false); + Sensors::setConnectionStatusByType(Sensors::Type::OT_HEATING_PUMP_STARTS, false); + Sensors::setConnectionStatusByType(Sensors::Type::OT_DHW_PUMP_STARTS, false); + Sensors::setConnectionStatusByType(Sensors::Type::OT_BURNER_HOURS, false); + Sensors::setConnectionStatusByType(Sensors::Type::OT_DHW_BURNER_HOURS, false); + Sensors::setConnectionStatusByType(Sensors::Type::OT_HEATING_PUMP_HOURS, false); + Sensors::setConnectionStatusByType(Sensors::Type::OT_DHW_PUMP_HOURS, false); + this->initialized = false; this->disconnectedTime = millis(); vars.slave.connected = false; @@ -310,34 +349,60 @@ protected: Log.sinfoln(FPSTR(L_OT_DHW), vars.master.dhw.enabled ? F("Enabled") : F("Disabled")); } - vars.slave.heating.active = CustomOpenTherm::isCentralHeatingActive(response); - vars.slave.dhw.active = settings.opentherm.options.dhwSupport ? CustomOpenTherm::isHotWaterActive(response) : false; - vars.slave.flame = CustomOpenTherm::isFlameOn(response); - vars.slave.cooling = CustomOpenTherm::isCoolingActive(response); - vars.slave.fault.active = CustomOpenTherm::isFault(response); - vars.slave.diag.active = CustomOpenTherm::isDiagnostic(response); - - Log.snoticeln( - FPSTR(L_OT), F("Received boiler status. Heating: %hhu; DHW: %hhu; flame: %hhu; cooling: %hhu; fault: %hhu; diag: %hhu"), - vars.slave.heating.active, vars.slave.dhw.active, - vars.slave.flame, vars.slave.cooling, vars.slave.fault.active, vars.slave.diag.active - ); - // These parameters will be updated every minute if (millis() - this->prevUpdateNonEssentialVars > 60000) { + // Set date & time + if (settings.opentherm.options.setDateAndTime) { + struct tm ti; + + if (getLocalTime(&ti)) { + if (this->setYear(&ti)) { + Log.sinfoln(FPSTR(L_OT), F("Year of date set successfully")); + + } else { + Log.sinfoln(FPSTR(L_OT), F("Failed set year of date")); + } + + if (this->setDayAndMonth(&ti)) { + Log.sinfoln(FPSTR(L_OT), F("Day and month of date set successfully")); + + } else { + Log.sinfoln(FPSTR(L_OT), F("Failed set day and month of date")); + } + + if (this->setTime(&ti)) { + Log.sinfoln(FPSTR(L_OT), F("Time set successfully")); + + } else { + Log.sinfoln(FPSTR(L_OT), F("Failed set time")); + } + } + } + + // Get min modulation level & max power if (this->updateMinModulationLevel()) { Log.snoticeln( FPSTR(L_OT), F("Received min modulation: %hhu%%, max power: %.2f kW"), vars.slave.modulation.min, vars.slave.power.max ); - if (settings.opentherm.maxModulation < vars.slave.modulation.min) { - settings.opentherm.maxModulation = vars.slave.modulation.min; + if (settings.heating.maxModulation < vars.slave.modulation.min) { + settings.heating.maxModulation = vars.slave.modulation.min; fsSettings.update(); Log.swarningln( - FPSTR(L_SETTINGS_OT), F("Updated min modulation: %hhu%%"), - settings.opentherm.maxModulation + FPSTR(L_SETTINGS_HEATING), F("Updated min modulation: %hhu%%"), + settings.heating.maxModulation + ); + } + + if (settings.dhw.maxModulation < vars.slave.modulation.min) { + settings.dhw.maxModulation = vars.slave.modulation.min; + fsSettings.update(); + + Log.swarningln( + FPSTR(L_SETTINGS_DHW), F("Updated min modulation: %hhu%%"), + settings.dhw.maxModulation ); } @@ -356,29 +421,6 @@ protected: Log.swarningln(FPSTR(L_OT), F("Failed receive min modulation and max power")); } - if (!vars.master.heating.enabled && settings.opentherm.options.modulationSyncWithHeating) { - if (this->setMaxModulationLevel(0)) { - Log.snoticeln(FPSTR(L_OT), F("Set max modulation: 0% (response: %hhu%%)"), vars.slave.modulation.max); - - } else { - Log.swarningln(FPSTR(L_OT), F("Failed set max modulation: 0% (response: %hhu%%)"), vars.slave.modulation.max); - } - - } else { - if (this->setMaxModulationLevel(settings.opentherm.maxModulation)) { - Log.snoticeln( - FPSTR(L_OT), F("Set max modulation: %hhu%% (response: %hhu%%)"), - settings.opentherm.maxModulation, vars.slave.modulation.max - ); - - } else { - Log.swarningln( - FPSTR(L_OT), F("Failed set max modulation: %hhu%% (response: %hhu%%)"), - settings.opentherm.maxModulation, vars.slave.modulation.max - ); - } - } - // Get DHW min/max temp (if necessary) if (settings.opentherm.options.dhwSupport && settings.opentherm.options.getMinMaxTemp) { @@ -503,9 +545,138 @@ protected: vars.slave.diag.code = 0; } + // Update burner starts + if (Sensors::getAmountByType(Sensors::Type::OT_BURNER_STARTS, true)) { + if (this->updateBurnerStarts()) { + Log.snoticeln(FPSTR(L_OT), F("Received burner starts: %hu"), vars.slave.stats.burnerStarts); + + Sensors::setValueByType( + Sensors::Type::OT_BURNER_STARTS, vars.slave.stats.burnerStarts, + Sensors::ValueType::PRIMARY, true, true + ); + } + } + + // Update DHW burner starts + if (Sensors::getAmountByType(Sensors::Type::OT_DHW_BURNER_STARTS, true)) { + if (this->updateDhwBurnerStarts()) { + Log.snoticeln(FPSTR(L_OT), F("Received DHW burner starts: %hu"), vars.slave.stats.dhwBurnerStarts); + + Sensors::setValueByType( + Sensors::Type::OT_DHW_BURNER_STARTS, vars.slave.stats.dhwBurnerStarts, + Sensors::ValueType::PRIMARY, true, true + ); + } + } + + // Update heating pump starts + if (Sensors::getAmountByType(Sensors::Type::OT_HEATING_PUMP_STARTS, true)) { + if (this->updateHeatingPumpStarts()) { + Log.snoticeln(FPSTR(L_OT), F("Received heating pump starts: %hu"), vars.slave.stats.heatingPumpStarts); + + Sensors::setValueByType( + Sensors::Type::OT_HEATING_PUMP_STARTS, vars.slave.stats.heatingPumpStarts, + Sensors::ValueType::PRIMARY, true, true + ); + } + } + + // Update DHW pump starts + if (Sensors::getAmountByType(Sensors::Type::OT_DHW_PUMP_STARTS, true)) { + if (this->updateDhwPumpStarts()) { + Log.snoticeln(FPSTR(L_OT), F("Received DHW pump starts: %hu"), vars.slave.stats.dhwPumpStarts); + + Sensors::setValueByType( + Sensors::Type::OT_DHW_PUMP_STARTS, vars.slave.stats.dhwPumpStarts, + Sensors::ValueType::PRIMARY, true, true + ); + } + } + + // Update burner hours + if (Sensors::getAmountByType(Sensors::Type::OT_BURNER_HOURS, true)) { + if (this->updateBurnerHours()) { + Log.snoticeln(FPSTR(L_OT), F("Received burner hours: %hu"), vars.slave.stats.burnerHours); + + Sensors::setValueByType( + Sensors::Type::OT_BURNER_HOURS, vars.slave.stats.burnerHours, + Sensors::ValueType::PRIMARY, true, true + ); + } + } + + // Update DHW burner hours + if (Sensors::getAmountByType(Sensors::Type::OT_DHW_BURNER_HOURS, true)) { + if (this->updateDhwBurnerHours()) { + Log.snoticeln(FPSTR(L_OT), F("Received DHW burner hours: %hu"), vars.slave.stats.dhwBurnerHours); + + Sensors::setValueByType( + Sensors::Type::OT_DHW_BURNER_HOURS, vars.slave.stats.dhwBurnerHours, + Sensors::ValueType::PRIMARY, true, true + ); + } + } + + // Update heating pump hours + if (Sensors::getAmountByType(Sensors::Type::OT_HEATING_PUMP_HOURS, true)) { + if (this->updateHeatingPumpHours()) { + Log.snoticeln(FPSTR(L_OT), F("Received heating pump hours: %hu"), vars.slave.stats.heatingPumpHours); + + Sensors::setValueByType( + Sensors::Type::OT_HEATING_PUMP_HOURS, vars.slave.stats.heatingPumpHours, + Sensors::ValueType::PRIMARY, true, true + ); + } + } + + // Update DHW pump hours + if (Sensors::getAmountByType(Sensors::Type::OT_DHW_PUMP_HOURS, true)) { + if (this->updateDhwPumpHours()) { + Log.snoticeln(FPSTR(L_OT), F("Received DHW pump hours: %hu"), vars.slave.stats.dhwPumpHours); + + Sensors::setValueByType( + Sensors::Type::OT_DHW_PUMP_HOURS, vars.slave.stats.dhwPumpHours, + Sensors::ValueType::PRIMARY, true, true + ); + } + } + + // Auto fault reset + if (settings.opentherm.options.autoFaultReset && vars.slave.fault.active && !vars.actions.resetFault) { + vars.actions.resetFault = true; + } + + // Auto diag reset + if (settings.opentherm.options.autoDiagReset && vars.slave.diag.active && !vars.actions.resetDiagnostic) { + vars.actions.resetDiagnostic = true; + } + this->prevUpdateNonEssentialVars = millis(); } + // Set max modulation level + uint8_t targetMaxModulation = vars.slave.modulation.max; + if (vars.slave.heating.active) { + targetMaxModulation = settings.heating.maxModulation; + + } else if (vars.slave.dhw.active) { + targetMaxModulation = settings.dhw.maxModulation; + } + + if (vars.slave.modulation.max != targetMaxModulation) { + if (this->setMaxModulationLevel(targetMaxModulation)) { + Log.snoticeln( + FPSTR(L_OT), F("Set max modulation: %hhu%% (response: %hhu%%)"), + targetMaxModulation, vars.slave.modulation.max + ); + + } else { + Log.swarningln( + FPSTR(L_OT), F("Failed set max modulation: %hhu%% (response: %hhu%%)"), + targetMaxModulation, vars.slave.modulation.max + ); + } + } // Update modulation level if ( @@ -1179,17 +1350,17 @@ protected: bool needSetDhwTemp(const float target) { return millis() - this->dhwSetTempTime > this->dhwSetTempInterval - || fabsf(target - vars.slave.dhw.targetTemp) > 0.001f; + || fabsf(target - vars.slave.dhw.targetTemp) > 0.05f; } bool needSetHeatingTemp(const float target) { return millis() - this->heatingSetTempTime > this->heatingSetTempInterval - || fabsf(target - vars.slave.heating.targetTemp) > 0.001f; + || fabsf(target - vars.slave.heating.targetTemp) > 0.05f; } bool needSetCh2Temp(const float target) { return millis() - this->ch2SetTempTime > this->ch2SetTempInterval - || fabsf(target - vars.slave.ch2.targetTemp) > 0.001f; + || fabsf(target - vars.slave.ch2.targetTemp) > 0.05f; } bool updateSlaveConfig() { @@ -1228,6 +1399,65 @@ protected: return true; } + bool setYear(const struct tm *ptm) { + const unsigned int request = (ptm->tm_year + 1900) & 0xFFFF; + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::WRITE_DATA, + OpenThermMessageID::Year, + request + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::Year)) { + return false; + } + + return CustomOpenTherm::getUInt(response) == request; + } + + bool setDayAndMonth(const struct tm *ptm) { + const unsigned int request = ((ptm->tm_mon + 1) & 0xFF << 8) + | (ptm->tm_mday & 0xFF); + + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::WRITE_DATA, + OpenThermMessageID::Date, + request + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::Date)) { + return false; + } + + return CustomOpenTherm::getUInt(response) == request; + } + + bool setTime(const struct tm *ptm) { + const uint8_t dayOfWeek = ptm->tm_wday == 0 ? 6 : ptm->tm_wday - 1; + const unsigned int request = ((dayOfWeek & 0x07) << 13) + | ((ptm->tm_hour & 0x1F) << 8) + | (ptm->tm_min & 0x3F); + + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::WRITE_DATA, + OpenThermMessageID::DayTime, + request + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::DayTime)) { + return false; + } + + return CustomOpenTherm::getUInt(response) == request; + } bool setMaxModulationLevel(const uint8_t value) { const unsigned int request = CustomOpenTherm::toFloat(value); @@ -1350,7 +1580,7 @@ protected: return CustomOpenTherm::getUInt(response) == request; } - bool setMaxHeatingTemp(const uint8_t temperature) { + bool setMaxHeatingTemp(const float temperature) { const unsigned int request = CustomOpenTherm::temperatureToData(temperature); const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( OpenThermMessageType::WRITE_DATA, @@ -1640,6 +1870,158 @@ protected: return true; } + bool updateBurnerStarts() { + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::READ_DATA, + OpenThermMessageID::SuccessfulBurnerStarts, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::SuccessfulBurnerStarts)) { + return false; + } + + vars.slave.stats.burnerStarts = CustomOpenTherm::getUInt(response); + + return true; + } + + bool updateDhwBurnerStarts() { + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::READ_DATA, + OpenThermMessageID::DHWBurnerStarts, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::DHWBurnerStarts)) { + return false; + } + + vars.slave.stats.dhwBurnerStarts = CustomOpenTherm::getUInt(response); + + return true; + } + + bool updateHeatingPumpStarts() { + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::READ_DATA, + OpenThermMessageID::CHPumpStarts, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::CHPumpStarts)) { + return false; + } + + vars.slave.stats.heatingPumpStarts = CustomOpenTherm::getUInt(response); + + return true; + } + + bool updateDhwPumpStarts() { + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::READ_DATA, + OpenThermMessageID::DHWPumpValveStarts, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::DHWPumpValveStarts)) { + return false; + } + + vars.slave.stats.dhwPumpStarts = CustomOpenTherm::getUInt(response); + + return true; + } + + bool updateBurnerHours() { + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::READ_DATA, + OpenThermMessageID::BurnerOperationHours, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::BurnerOperationHours)) { + return false; + } + + vars.slave.stats.burnerHours = CustomOpenTherm::getUInt(response); + + return true; + } + + bool updateDhwBurnerHours() { + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::READ_DATA, + OpenThermMessageID::DHWBurnerOperationHours, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::DHWBurnerOperationHours)) { + return false; + } + + vars.slave.stats.dhwBurnerHours = CustomOpenTherm::getUInt(response); + + return true; + } + + bool updateHeatingPumpHours() { + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::READ_DATA, + OpenThermMessageID::CHPumpOperationHours, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::CHPumpOperationHours)) { + return false; + } + + vars.slave.stats.heatingPumpHours = CustomOpenTherm::getUInt(response); + + return true; + } + + bool updateDhwPumpHours() { + const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( + OpenThermRequestType::READ_DATA, + OpenThermMessageID::DHWPumpValveOperationHours, + 0 + )); + + if (!CustomOpenTherm::isValidResponse(response)) { + return false; + + } else if (!CustomOpenTherm::isValidResponseId(response, OpenThermMessageID::DHWPumpValveOperationHours)) { + return false; + } + + vars.slave.stats.dhwPumpHours = CustomOpenTherm::getUInt(response); + + return true; + } + bool updateModulationLevel() { const unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( OpenThermRequestType::READ_DATA, diff --git a/src/RegulatorTask.h b/src/RegulatorTask.h index 7386115..c6581bd 100644 --- a/src/RegulatorTask.h +++ b/src/RegulatorTask.h @@ -58,11 +58,11 @@ protected: this->hysteresis(); vars.master.heating.targetTemp = settings.heating.target; - vars.master.heating.setpointTemp = constrain( + vars.master.heating.setpointTemp = roundf(constrain( this->getHeatingSetpointTemp(), this->getHeatingMinSetpointTemp(), this->getHeatingMaxSetpointTemp() - ); + ), 0); Sensors::setValueByType( Sensors::Type::HEATING_SETPOINT_TEMP, vars.master.heating.setpointTemp, diff --git a/src/Sensors.h b/src/Sensors.h index dadb63e..4937d79 100644 --- a/src/Sensors.h +++ b/src/Sensors.h @@ -25,6 +25,15 @@ public: OT_SOLAR_COLLECTOR_TEMP = 16, OT_FAN_SPEED_SETPOINT = 17, OT_FAN_SPEED_CURRENT = 18, + + OT_BURNER_STARTS = 19, + OT_DHW_BURNER_STARTS = 20, + OT_HEATING_PUMP_STARTS = 21, + OT_DHW_PUMP_STARTS = 22, + OT_BURNER_HOURS = 23, + OT_DHW_BURNER_HOURS = 24, + OT_HEATING_PUMP_HOURS = 25, + OT_DHW_PUMP_HOURS = 26, NTC_10K_TEMP = 50, DALLAS_TEMP = 51, @@ -46,6 +55,7 @@ public: EXHAUST_TEMP = 7, MODULATION_LEVEL = 8, + NUMBER = 247, POWER_FACTOR = 248, POWER = 249, FAN_SPEED = 250, diff --git a/src/Settings.h b/src/Settings.h index 9d045fe..1442534 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -59,7 +59,6 @@ struct Settings { byte rxLedGpio = DEFAULT_OT_RX_LED_GPIO; uint8_t memberId = 0; uint8_t flags = 0; - uint8_t maxModulation = 100; float minPower = 0.0f; float maxPower = 0.0f; @@ -72,9 +71,12 @@ struct Settings { bool heatingToCh2 = false; bool dhwToCh2 = false; bool dhwBlocking = false; - bool modulationSyncWithHeating = false; bool maxTempSyncWithTargetTemp = true; bool getMinMaxTemp = true; + bool ignoreDiagState = false; + bool autoFaultReset = false; + bool autoDiagReset = false; + bool setDateAndTime = false; bool nativeHeatingControl = false; bool immergasFix = false; } options; @@ -104,6 +106,7 @@ struct Settings { float turboFactor = 7.5f; byte minTemp = DEFAULT_HEATING_MIN_TEMP; byte maxTemp = DEFAULT_HEATING_MAX_TEMP; + uint8_t maxModulation = 100; } heating; struct { @@ -111,6 +114,7 @@ struct Settings { float target = DEFAULT_DHW_TARGET_TEMP; byte minTemp = DEFAULT_DHW_MIN_TEMP; byte maxTemp = DEFAULT_DHW_MAX_TEMP; + uint8_t maxModulation = 100; } dhw; struct { @@ -351,6 +355,17 @@ struct Variables { uint16_t supply = 0; } fanSpeed; + struct { + uint16_t burnerStarts = 0; + uint16_t dhwBurnerStarts = 0; + uint16_t heatingPumpStarts = 0; + uint16_t dhwPumpStarts = 0; + uint16_t burnerHours = 0; + uint16_t dhwBurnerHours = 0; + uint16_t heatingPumpHours = 0; + uint16_t dhwPumpHours = 0; + } stats; + struct { bool active = false; bool enabled = false; diff --git a/src/defines.h b/src/defines.h index f43ea4d..f5e0de9 100644 --- a/src/defines.h +++ b/src/defines.h @@ -16,7 +16,7 @@ #define THERMOSTAT_INDOOR_DEFAULT_TEMP 20 #define THERMOSTAT_INDOOR_MIN_TEMP 5 -#define THERMOSTAT_INDOOR_MAX_TEMP 30 +#define THERMOSTAT_INDOOR_MAX_TEMP 40 #define DEFAULT_NTC_NOMINAL_RESISTANCE 10000.0f #define DEFAULT_NTC_NOMINAL_TEMP 25.0f diff --git a/src/strings.h b/src/strings.h index e2bd7ff..b924369 100644 --- a/src/strings.h +++ b/src/strings.h @@ -42,6 +42,8 @@ const char S_ANTI_STUCK_TIME[] PROGMEM = "antiStuckTime"; const char S_AP[] PROGMEM = "ap"; const char S_APP_VERSION[] PROGMEM = "appVersion"; const char S_AUTH[] PROGMEM = "auth"; +const char S_AUTO_DIAG_RESET[] PROGMEM = "autoDiagReset"; +const char S_AUTO_FAULT_RESET[] PROGMEM = "autoFaultReset"; const char S_BACKTRACE[] PROGMEM = "backtrace"; const char S_BATTERY[] PROGMEM = "battery"; const char S_BAUDRATE[] PROGMEM = "baudrate"; @@ -102,6 +104,7 @@ const char S_HOSTNAME[] PROGMEM = "hostname"; const char S_HUMIDITY[] PROGMEM = "humidity"; const char S_HYSTERESIS[] PROGMEM = "hysteresis"; const char S_ID[] PROGMEM = "id"; +const char S_IGNORE_DIAG_STATE[] PROGMEM = "ignoreDiagState"; const char S_IMMERGAS_FIX[] PROGMEM = "immergasFix"; const char S_INDOOR_TEMP[] PROGMEM = "indoorTemp"; const char S_INDOOR_TEMP_CONTROL[] PROGMEM = "indoorTempControl"; @@ -131,7 +134,6 @@ const char S_MIN_POWER[] PROGMEM = "minPower"; const char S_MIN_TEMP[] PROGMEM = "minTemp"; const char S_MODEL[] PROGMEM = "model"; const char S_MODULATION[] PROGMEM = "modulation"; -const char S_MODULATION_SYNC_WITH_HEATING[] PROGMEM = "modulationSyncWithHeating"; const char S_MQTT[] PROGMEM = "mqtt"; const char S_NAME[] PROGMEM = "name"; const char S_NATIVE_HEATING_CONTROL[] PROGMEM = "nativeHeatingControl"; @@ -172,6 +174,7 @@ const char S_SENSORS[] PROGMEM = "sensors"; const char S_SERIAL[] PROGMEM = "serial"; const char S_SERVER[] PROGMEM = "server"; const char S_SETTINGS[] PROGMEM = "settings"; +const char S_SET_DATE_AND_TIME[] PROGMEM = "setDateAndTime"; const char S_SHIFT[] PROGMEM = "shift"; const char S_SIGNAL_QUALITY[] PROGMEM = "signalQuality"; const char S_SIZE[] PROGMEM = "size"; diff --git a/src/utils.h b/src/utils.h index c26c054..b2404e6 100644 --- a/src/utils.h +++ b/src/utils.h @@ -449,7 +449,6 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) { opentherm[FPSTR(S_RX_LED_GPIO)] = src.opentherm.rxLedGpio; opentherm[FPSTR(S_MEMBER_ID)] = src.opentherm.memberId; opentherm[FPSTR(S_FLAGS)] = src.opentherm.flags; - opentherm[FPSTR(S_MAX_MODULATION)] = src.opentherm.maxModulation; opentherm[FPSTR(S_MIN_POWER)] = roundf(src.opentherm.minPower, 2); opentherm[FPSTR(S_MAX_POWER)] = roundf(src.opentherm.maxPower, 2); @@ -462,9 +461,12 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) { otOptions[FPSTR(S_HEATING_TO_CH2)] = src.opentherm.options.heatingToCh2; otOptions[FPSTR(S_DHW_TO_CH2)] = src.opentherm.options.dhwToCh2; otOptions[FPSTR(S_DHW_BLOCKING)] = src.opentherm.options.dhwBlocking; - otOptions[FPSTR(S_MODULATION_SYNC_WITH_HEATING)] = src.opentherm.options.modulationSyncWithHeating; otOptions[FPSTR(S_MAX_TEMP_SYNC_WITH_TARGET_TEMP)] = src.opentherm.options.maxTempSyncWithTargetTemp; otOptions[FPSTR(S_GET_MIN_MAX_TEMP)] = src.opentherm.options.getMinMaxTemp; + otOptions[FPSTR(S_IGNORE_DIAG_STATE)] = src.opentherm.options.ignoreDiagState; + otOptions[FPSTR(S_AUTO_FAULT_RESET)] = src.opentherm.options.autoFaultReset; + otOptions[FPSTR(S_AUTO_DIAG_RESET)] = src.opentherm.options.autoDiagReset; + otOptions[FPSTR(S_SET_DATE_AND_TIME)] = src.opentherm.options.setDateAndTime; otOptions[FPSTR(S_NATIVE_HEATING_CONTROL)] = src.opentherm.options.nativeHeatingControl; otOptions[FPSTR(S_IMMERGAS_FIX)] = src.opentherm.options.immergasFix; @@ -491,12 +493,14 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) { heating[FPSTR(S_TURBO_FACTOR)] = roundf(src.heating.turboFactor, 3); heating[FPSTR(S_MIN_TEMP)] = src.heating.minTemp; heating[FPSTR(S_MAX_TEMP)] = src.heating.maxTemp; + heating[FPSTR(S_MAX_MODULATION)] = src.heating.maxModulation; auto dhw = dst[FPSTR(S_DHW)].to(); dhw[FPSTR(S_ENABLED)] = src.dhw.enabled; dhw[FPSTR(S_TARGET)] = roundf(src.dhw.target, 1); dhw[FPSTR(S_MIN_TEMP)] = src.dhw.minTemp; dhw[FPSTR(S_MAX_TEMP)] = src.dhw.maxTemp; + dhw[FPSTR(S_MAX_MODULATION)] = src.dhw.maxModulation; auto equitherm = dst[FPSTR(S_EQUITHERM)].to(); equitherm[FPSTR(S_ENABLED)] = src.equitherm.enabled; @@ -812,15 +816,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false } } - if (!src[FPSTR(S_OPENTHERM)][FPSTR(S_MAX_MODULATION)].isNull()) { - unsigned char value = src[FPSTR(S_OPENTHERM)][FPSTR(S_MAX_MODULATION)].as(); - - if (value > 0 && value <= 100 && value != dst.opentherm.maxModulation) { - dst.opentherm.maxModulation = value; - changed = true; - } - } - if (!src[FPSTR(S_OPENTHERM)][FPSTR(S_MIN_POWER)].isNull()) { float value = src[FPSTR(S_OPENTHERM)][FPSTR(S_MIN_POWER)].as(); @@ -929,15 +924,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false } } - if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_MODULATION_SYNC_WITH_HEATING)].is()) { - bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_MODULATION_SYNC_WITH_HEATING)].as(); - - if (value != dst.opentherm.options.modulationSyncWithHeating) { - dst.opentherm.options.modulationSyncWithHeating = value; - changed = true; - } - } - if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_MAX_TEMP_SYNC_WITH_TARGET_TEMP)].is()) { bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_MAX_TEMP_SYNC_WITH_TARGET_TEMP)].as(); @@ -956,6 +942,42 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false } } + if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_IGNORE_DIAG_STATE)].is()) { + bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_IGNORE_DIAG_STATE)].as(); + + if (value != dst.opentherm.options.ignoreDiagState) { + dst.opentherm.options.ignoreDiagState = value; + changed = true; + } + } + + if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_AUTO_FAULT_RESET)].is()) { + bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_AUTO_FAULT_RESET)].as(); + + if (value != dst.opentherm.options.autoFaultReset) { + dst.opentherm.options.autoFaultReset = value; + changed = true; + } + } + + if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_AUTO_DIAG_RESET)].is()) { + bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_AUTO_DIAG_RESET)].as(); + + if (value != dst.opentherm.options.autoDiagReset) { + dst.opentherm.options.autoDiagReset = value; + changed = true; + } + } + + if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_SET_DATE_AND_TIME)].is()) { + bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_SET_DATE_AND_TIME)].as(); + + if (value != dst.opentherm.options.setDateAndTime) { + dst.opentherm.options.setDateAndTime = value; + changed = true; + } + } + if (src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_NATIVE_HEATING_CONTROL)].is()) { bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_NATIVE_HEATING_CONTROL)].as(); @@ -1311,6 +1333,16 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false } + if (!src[FPSTR(S_HEATING)][FPSTR(S_MAX_MODULATION)].isNull()) { + unsigned char value = src[FPSTR(S_HEATING)][FPSTR(S_MAX_MODULATION)].as(); + + if (value > 0 && value <= 100 && value != dst.heating.maxModulation) { + dst.heating.maxModulation = value; + changed = true; + } + } + + // dhw if (src[FPSTR(S_DHW)][FPSTR(S_ENABLED)].is()) { bool value = src[FPSTR(S_DHW)][FPSTR(S_ENABLED)].as(); @@ -1344,6 +1376,15 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false changed = true; } + if (!src[FPSTR(S_DHW)][FPSTR(S_MAX_MODULATION)].isNull()) { + unsigned char value = src[FPSTR(S_DHW)][FPSTR(S_MAX_MODULATION)].as(); + + if (value > 0 && value <= 100 && value != dst.dhw.maxModulation) { + dst.dhw.maxModulation = value; + changed = true; + } + } + if (!safe) { // external pump @@ -1702,6 +1743,7 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se case static_cast(Sensors::Purpose::EXHAUST_TEMP): case static_cast(Sensors::Purpose::MODULATION_LEVEL): + case static_cast(Sensors::Purpose::NUMBER): case static_cast(Sensors::Purpose::POWER_FACTOR): case static_cast(Sensors::Purpose::POWER): case static_cast(Sensors::Purpose::FAN_SPEED): @@ -1746,6 +1788,15 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se case static_cast(Sensors::Type::OT_FAN_SPEED_SETPOINT): case static_cast(Sensors::Type::OT_FAN_SPEED_CURRENT): + case static_cast(Sensors::Type::OT_BURNER_STARTS): + case static_cast(Sensors::Type::OT_DHW_BURNER_STARTS): + case static_cast(Sensors::Type::OT_HEATING_PUMP_STARTS): + case static_cast(Sensors::Type::OT_DHW_PUMP_STARTS): + case static_cast(Sensors::Type::OT_BURNER_HOURS): + case static_cast(Sensors::Type::OT_DHW_BURNER_HOURS): + case static_cast(Sensors::Type::OT_HEATING_PUMP_HOURS): + case static_cast(Sensors::Type::OT_DHW_PUMP_HOURS): + case static_cast(Sensors::Type::NTC_10K_TEMP): case static_cast(Sensors::Type::DALLAS_TEMP): case static_cast(Sensors::Type::BLUETOOTH): diff --git a/src_data/locales/en.json b/src_data/locales/en.json index 6f7b6e1..31fb86c 100644 --- a/src_data/locales/en.json +++ b/src_data/locales/en.json @@ -201,6 +201,7 @@ "dhwFlowRate": "DHW, flow rate", "exhaustTemp": "Exhaust temperature", "modLevel": "Modulation level (in percents)", + "number": "Number (raw)", "powerFactor": "Power (in percent)", "power": "Power (in kWt)", "fanSpeed": "Fan speed", @@ -231,6 +232,14 @@ "otSolarCollectorTemp": "OpenTherm, solar collector temp", "otFanSpeedSetpoint": "OpenTherm, setpoint fan speed", "otFanSpeedCurrent": "OpenTherm, current fan speed", + "otBurnerStarts": "OpenTherm, number of burner starts", + "otDhwBurnerStarts": "OpenTherm, number of burner starts (DHW)", + "otHeatingPumpStarts": "OpenTherm, number of pump starts (heating)", + "otDhwPumpStarts": "OpenTherm, number of pump starts (DHW)", + "otBurnerHours": "OpenTherm, number of burner operating hours", + "otDhwBurnerHours": "OpenTherm, number of burner operating hours (DHW)", + "otHeatingPumpHours": "OpenTherm, number of pump operating hours (heating)", + "otDhwPumpHours": "OpenTherm, number of pump operating hours (DHW)", "ntcTemp": "NTC sensor", "dallasTemp": "DALLAS sensor", @@ -292,6 +301,7 @@ "min": "Minimum temperature", "max": "Maximum temperature" }, + "maxModulation": "Max modulation level", "portal": { "login": "Login", @@ -390,7 +400,6 @@ "ledGpio": "RX LED GPIO", "memberId": "Master member ID", "flags": "Master flags", - "maxMod": "Max modulation level", "minPower": { "title": "Min boiler power (kW)", "note": "This value is at 0-1% boiler modulation level. Typically found in the boiler specification as \"minimum useful heat output\"." @@ -410,9 +419,12 @@ "heatingToCh2": "Duplicate heating to CH2", "dhwToCh2": "Duplicate DHW to CH2", "dhwBlocking": "DHW blocking", - "modulationSyncWithHeating": "Sync modulation with heating", "maxTempSyncWithTargetTemp": "Sync max heating temp with target temp", "getMinMaxTemp": "Get min/max temp from boiler", + "ignoreDiagState": "Ignore diag state", + "autoFaultReset": "Auto fault reset (not recommended!)", + "autoDiagReset": "Auto diag reset (not recommended!)", + "setDateAndTime": "Set date & time on boiler", "immergasFix": "Fix for Immergas boilers" }, diff --git a/src_data/locales/it.json b/src_data/locales/it.json index c388f43..3a34983 100644 --- a/src_data/locales/it.json +++ b/src_data/locales/it.json @@ -201,6 +201,7 @@ "dhwFlowRate": "ACS, prelievo", "exhaustTemp": "Temperatura fumi", "modLevel": "Livello Modulazione (%)", + "number": "Numero (raw)", "powerFactor": "Potenza (%)", "power": "Potenza (in kW)", "fanSpeed": "Velocità ventilatore", @@ -231,6 +232,14 @@ "otSolarCollectorTemp": "OpenTherm, temp collettore solare", "otFanSpeedSetpoint": "OpenTherm, velocità ventola impostata", "otFanSpeedCurrent": "OpenTherm, velocità ventola attuale", + "otBurnerStarts": "OpenTherm, numero di avviamenti del bruciatore", + "otDhwBurnerStarts": "OpenTherm, numero di avviamenti del bruciatore (ACS)", + "otHeatingPumpStarts": "OpenTherm, numero di avviamenti della pompa (riscaldamento)", + "otDhwPumpStarts": "OpenTherm, numero di avviamenti della pompa (ACS)", + "otBurnerHours": "OpenTherm, numero di ore di funzionamento del bruciatore", + "otDhwBurnerHours": "OpenTherm, numero di ore di funzionamento del bruciatore (ACS)", + "otHeatingPumpHours": "OpenTherm, numero di ore di funzionamento della pompa (riscaldamento)", + "otDhwPumpHours": "OpenTherm, numero di ore di funzionamento della pompa (ACS)", "ntcTemp": "Sensore NTC", "dallasTemp": "Sensore DALLAS", @@ -292,6 +301,7 @@ "min": "Temperatura minima", "max": "Temperatura massima" }, + "maxModulation": "Max livello modulazione", "portal": { "login": "Login", @@ -390,7 +400,6 @@ "ledGpio": "RX LED GPIO", "memberId": "Master member ID", "flags": "Master flags", - "maxMod": "Max livello modulazione", "minPower": { "title": "Potenza minima caldaia (kW)", "note": "Questo valore corrisponde allo livello 0-1% di modulazione della caldaia. Di solito si trova nelle specifiche delle caldaia come \"potenza minima disponibile\"." @@ -410,9 +419,12 @@ "heatingToCh2": "Riproduci riscaldamento su CH2", "dhwToCh2": "Riproduci ACS su CH2", "dhwBlocking": "Bloccare ACS", - "modulationSyncWithHeating": "Sincronizzare modulazione con caldaia", "maxTempSyncWithTargetTemp": "Sincronizza la temperatura massima di riscaldamento con la temperatura target", "getMinMaxTemp": "Prendi temp min/max dalla caldaia", + "ignoreDiagState": "Ignora lo stato diagnostico", + "autoFaultReset": "Ripristino automatico degli errori (sconsigliato!)", + "autoDiagReset": "Ripristino diagnostico automatica (sconsigliato!)", + "setDateAndTime": "Imposta data e ora sulla caldaia", "immergasFix": "Fix per caldiaie Immergas" }, diff --git a/src_data/locales/ru.json b/src_data/locales/ru.json index 1b3dcf4..d41f1bd 100644 --- a/src_data/locales/ru.json +++ b/src_data/locales/ru.json @@ -201,6 +201,7 @@ "dhwFlowRate": "ГВС, расход/скорость потока", "exhaustTemp": "Температура выхлопных газов", "modLevel": "Уровень модуляции (в процентах)", + "number": "Число (raw)", "powerFactor": "Мощность (в процентах)", "power": "Мощность (в кВт)", "fanSpeed": "Скорость вентилятора", @@ -231,6 +232,14 @@ "otSolarCollectorTemp": "OpenTherm, темп. солн. коллектора", "otFanSpeedSetpoint": "OpenTherm, установленная мощн. вентилятора", "otFanSpeedCurrent": "OpenTherm, текущая мощн. вентилятора", + "otBurnerStarts": "OpenTherm, кол-во запусков горелки", + "otDhwBurnerStarts": "OpenTherm, кол-во запусков горелки (ГВС)", + "otHeatingPumpStarts": "OpenTherm, кол-во запусков насоса (отопление)", + "otDhwPumpStarts": "OpenTherm, кол-во запусков насоса (ГВС)", + "otBurnerHours": "OpenTherm, кол-во часов работы горелки", + "otDhwBurnerHours": "OpenTherm, кол-во часов работы горелки (ГВС)", + "otHeatingPumpHours": "OpenTherm, кол-во часов работы насоса (отопление)", + "otDhwPumpHours": "OpenTherm, кол-во часов работы насоса (ГВС)", "ntcTemp": "NTC датчик", "dallasTemp": "DALLAS датчик", @@ -292,6 +301,7 @@ "min": "Мин. температура", "max": "Макс. температура" }, + "maxModulation": "Макс. уровень модуляции", "portal": { "login": "Логин", @@ -390,7 +400,6 @@ "ledGpio": "RX LED GPIO", "memberId": "Master member ID", "flags": "Master flags", - "maxMod": "Макс. уровень модуляции", "minPower": { "title": "Мин. мощность котла (кВт)", "note": "Это значение соответствует уровню модуляции котла 0–1%. Обычно можно найти в спецификации котла как \"минимальная полезная тепловая мощность\"." @@ -410,9 +419,12 @@ "heatingToCh2": "Дублировать параметры отопления в канал 2", "dhwToCh2": "Дублировать параметры ГВС в канал 2", "dhwBlocking": "DHW blocking", - "modulationSyncWithHeating": "Синхронизировать модуляцию с отоплением", "maxTempSyncWithTargetTemp": "Синхронизировать макс. темп. отопления с целевой темп.", "getMinMaxTemp": "Получать мин. и макс. температуру от котла", + "ignoreDiagState": "Игнорировать состояние диагностики", + "autoFaultReset": "Автоматический сброс ошибок (не рекомендуется!)", + "autoDiagReset": "Автоматический сброс диагностики (не рекомендуется!)", + "setDateAndTime": "Устанавливать время и дату на котле", "immergasFix": "Фикс для котлов Immergas" }, diff --git a/src_data/pages/sensors.html b/src_data/pages/sensors.html index 5f298fb..7867652 100644 --- a/src_data/pages/sensors.html +++ b/src_data/pages/sensors.html @@ -69,6 +69,7 @@ + @@ -102,6 +103,14 @@ + + + + + + + + diff --git a/src_data/pages/settings.html b/src_data/pages/settings.html index 2c36304..a1d058a 100644 --- a/src_data/pages/settings.html +++ b/src_data/pages/settings.html @@ -202,6 +202,11 @@ + + @@ -226,6 +231,11 @@ + + @@ -480,11 +490,6 @@ settings.ot.flags - -
@@ -544,11 +549,6 @@ settings.ot.options.dhwBlocking - - + + + + + + + +