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)
This commit is contained in:
Yurii
2025-05-19 21:09:39 +03:00
parent 4b1b7f5857
commit 6872cad8ce
8 changed files with 324 additions and 0 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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;

View File

@@ -1723,6 +1723,7 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se
case static_cast<uint8_t>(Sensors::Purpose::EXHAUST_TEMP):
case static_cast<uint8_t>(Sensors::Purpose::MODULATION_LEVEL):
case static_cast<uint8_t>(Sensors::Purpose::NUMBER):
case static_cast<uint8_t>(Sensors::Purpose::POWER_FACTOR):
case static_cast<uint8_t>(Sensors::Purpose::POWER):
case static_cast<uint8_t>(Sensors::Purpose::FAN_SPEED):
@@ -1767,6 +1768,15 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se
case static_cast<uint8_t>(Sensors::Type::OT_FAN_SPEED_SETPOINT):
case static_cast<uint8_t>(Sensors::Type::OT_FAN_SPEED_CURRENT):
case static_cast<uint8_t>(Sensors::Type::OT_BURNER_STARTS):
case static_cast<uint8_t>(Sensors::Type::OT_DHW_BURNER_STARTS):
case static_cast<uint8_t>(Sensors::Type::OT_HEATING_PUMP_STARTS):
case static_cast<uint8_t>(Sensors::Type::OT_DHW_PUMP_STARTS):
case static_cast<uint8_t>(Sensors::Type::OT_BURNER_HOURS):
case static_cast<uint8_t>(Sensors::Type::OT_DHW_BURNER_HOURS):
case static_cast<uint8_t>(Sensors::Type::OT_HEATING_PUMP_HOURS):
case static_cast<uint8_t>(Sensors::Type::OT_DHW_PUMP_HOURS):
case static_cast<uint8_t>(Sensors::Type::NTC_10K_TEMP):
case static_cast<uint8_t>(Sensors::Type::DALLAS_TEMP):
case static_cast<uint8_t>(Sensors::Type::BLUETOOTH):