From 6872cad8ce9bc6e18104aa42bc70eab5db244148 Mon Sep 17 00:00:00 2001 From: Yurii Date: Mon, 19 May 2025 21:09:39 +0300 Subject: [PATCH] feat: added new purpose (number) for sensors and added polling for OpenTherm statistical IDs New sensor types: * Number of burner starts * Number of burner starts (DHW) * Number of pump starts (heating) * Number of pump starts (DHW) * Number of burner operating hours * Number of burner operating hours (DHW) * Number of pump operating hours (heating) * Number of pump operating hours (DHW) --- src/OpenThermTask.h | 257 ++++++++++++++++++++++++++++++++++++ src/Sensors.h | 10 ++ src/Settings.h | 11 ++ src/utils.h | 10 ++ src_data/locales/en.json | 9 ++ src_data/locales/it.json | 9 ++ src_data/locales/ru.json | 9 ++ src_data/pages/sensors.html | 9 ++ 8 files changed, 324 insertions(+) diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index 95e012b..2b66047 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -287,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; @@ -507,6 +516,102 @@ 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; @@ -1677,6 +1782,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/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 b705aba..3b204db 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -353,6 +353,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/utils.h b/src/utils.h index 6759195..d87c640 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1723,6 +1723,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): @@ -1767,6 +1768,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 fef099b..2c355ef 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", diff --git a/src_data/locales/it.json b/src_data/locales/it.json index 0ee8854..db7ffa8 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", diff --git a/src_data/locales/ru.json b/src_data/locales/ru.json index 07a3d11..bdb946c 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 датчик", diff --git a/src_data/pages/sensors.html b/src_data/pages/sensors.html index 90c78a2..c8b677d 100644 --- a/src_data/pages/sensors.html +++ b/src_data/pages/sensors.html @@ -69,6 +69,7 @@ + @@ -102,6 +103,14 @@ + + + + + + + +