11 Commits

Author SHA1 Message Date
Yurii
d88bd19b1a Merge 0bb05006f4 into 0e78e71493 2025-05-19 23:45:38 +03:00
Yurii
0bb05006f4 Merge branch 'master' into new-equitherm 2025-05-19 23:45:13 +03:00
Yurii
0e78e71493 refactor: more logs 2025-05-19 23:39:15 +03:00
Yurii
655313562d fix: typo 2025-05-19 23:28:55 +03:00
Yurii
c8e7724da8 refactor: cosmetic changes 2025-05-19 23:26:44 +03:00
Yurii
f986129c72 fix: set month of date to OT fixed 2025-05-19 23:16:34 +03:00
Yurii
5046bc0e8f Merge branch 'master' into new-equitherm 2025-05-19 22:49:09 +03:00
Yurii
7b31315242 feat: added OT option to set date and time on the boiler 2025-05-19 22:38:35 +03:00
Yurii
6872cad8ce 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)
2025-05-19 21:09:39 +03:00
Yurii
4b1b7f5857 feat: added OT options: ignore diag state, auto fault reset, auto diag reset 2025-05-18 16:47:28 +03:00
Yurii
a667317412 refactor: increased max. target indoor temp. from 30 to 40 degrees (for control by return temperature) 2025-05-18 16:01:04 +03:00
12 changed files with 548 additions and 14 deletions

View File

@@ -229,7 +229,13 @@ protected:
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);
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"),
@@ -281,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;
@@ -336,6 +351,35 @@ protected:
// 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"),
@@ -501,6 +545,136 @@ 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
);
} else {
Log.swarningln(FPSTR(L_OT), F("Failed receive burner starts"));
}
}
// 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
);
} else {
Log.swarningln(FPSTR(L_OT), F("Failed receive DHW burner starts"));
}
}
// 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
);
} else {
Log.swarningln(FPSTR(L_OT), F("Failed receive heating pump starts"));
}
}
// 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
);
} else {
Log.swarningln(FPSTR(L_OT), F("Failed receive DHW pump starts"));
}
}
// 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
);
} else {
Log.swarningln(FPSTR(L_OT), F("Failed receive burner hours"));
}
}
// 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
);
} else {
Log.swarningln(FPSTR(L_OT), F("Failed receive DHW burner hours"));
}
}
// 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
);
} else {
Log.swarningln(FPSTR(L_OT), F("Failed receive heating pump hours"));
}
}
// 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
);
} else {
Log.swarningln(FPSTR(L_OT), F("Failed receive DHW pump hours"));
}
}
// 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();
}
@@ -1249,6 +1423,64 @@ 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 uint8_t month = (ptm->tm_mon + 1) & 0xFF;
const unsigned int request = (month << 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);
@@ -1661,6 +1893,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

@@ -73,6 +73,10 @@ struct Settings {
bool dhwBlocking = 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;
@@ -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;

View File

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

View File

@@ -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";
@@ -171,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";

View File

@@ -463,6 +463,10 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
otOptions[FPSTR(S_DHW_BLOCKING)] = src.opentherm.options.dhwBlocking;
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;
@@ -938,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>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_IGNORE_DIAG_STATE)].as<bool>();
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>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_AUTO_FAULT_RESET)].as<bool>();
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>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_AUTO_DIAG_RESET)].as<bool>();
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>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_SET_DATE_AND_TIME)].as<bool>();
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>()) {
bool value = src[FPSTR(S_OPENTHERM)][FPSTR(S_OPTIONS)][FPSTR(S_NATIVE_HEATING_CONTROL)].as<bool>();
@@ -1703,6 +1743,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):
@@ -1747,6 +1788,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):

View File

@@ -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",
@@ -412,6 +421,10 @@
"dhwBlocking": "DHW blocking",
"maxTempSyncWithTargetTemp": "Sync max heating temp with target temp",
"getMinMaxTemp": "Get min/max temp from boiler",
"ignoreDiagState": "Ignore diag state",
"autoFaultReset": "Auto fault reset <small>(not recommended!)</small>",
"autoDiagReset": "Auto diag reset <small>(not recommended!)</small>",
"setDateAndTime": "Set date & time on boiler",
"immergasFix": "Fix for Immergas boilers"
},

View File

@@ -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",
@@ -412,6 +421,10 @@
"dhwBlocking": "Bloccare ACS",
"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 <small>(sconsigliato!)</small>",
"autoDiagReset": "Ripristino diagnostico automatica <small>(sconsigliato!)</small>",
"setDateAndTime": "Imposta data e ora sulla caldaia",
"immergasFix": "Fix per caldiaie Immergas"
},

View File

@@ -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 датчик",
@@ -412,6 +421,10 @@
"dhwBlocking": "DHW blocking",
"maxTempSyncWithTargetTemp": "Синхронизировать макс. темп. отопления с целевой темп.",
"getMinMaxTemp": "Получать мин. и макс. температуру от котла",
"ignoreDiagState": "Игнорировать состояние диагностики",
"autoFaultReset": "Автоматический сброс ошибок <small>(не рекомендуется!)</small>",
"autoDiagReset": "Автоматический сброс диагностики <small>(не рекомендуется!)</small>",
"setDateAndTime": "Устанавливать время и дату на котле",
"immergasFix": "Фикс для котлов Immergas"
},

View File

@@ -261,16 +261,16 @@
<details>
<summary><b data-i18n>dashboard.section.diag</b></summary>
<pre><b>Vendor:</b> <span class="sVendor"></span>
<b>Member ID:</b> <span class="sMemberId"></span>
<b>Flags:</b> <span class="sFlags"></span>
<b>Type:</b> <span class="sType"></span>
<b>AppVersion:</b> <span class="sAppVersion"></span>
<b>OT version:</b> <span class="sProtocolVersion"></span>
<b>Modulation limits:</b> <span class="sModMin"></span>...<span class="sAbsModMax"></span> %, curr. max: <span class="sModMax"></span> %
<b>Power limits:</b> <span class="sPowerMin"></span>...<span class="sPowerMax"></span> kW
<b>Heating limits:</b> <span class="sHeatMinTemp"></span>...<span class="sHeatMaxTemp"></span> <span class="tempUnit"></span>
<b>DHW limits:</b> <span class="sDhwMinTemp"></span>...<span class="sDhwMaxTemp"></span> <span class="tempUnit"></span></pre>
<pre><b>Vendor:</b> <span class="sVendor"></span>
<b>Member ID:</b> <span class="sMemberId"></span>
<b>Flags:</b> <span class="sFlags"></span>
<b>Type:</b> <span class="sType"></span>
<b>AppVersion:</b> <span class="sAppVersion"></span>
<b>OT version:</b> <span class="sProtocolVersion"></span>
<b>Modulation:</b> min: <span class="sModMin"></span> %, curr. max: <span class="sModMax"></span> %
<b>Power limits:</b> <span class="sPowerMin"></span>...<span class="sPowerMax"></span> kW
<b>Heating limits:</b> <span class="sHeatMinTemp"></span>...<span class="sHeatMaxTemp"></span> <span class="tempUnit"></span>
<b>DHW limits:</b> <span class="sDhwMinTemp"></span>...<span class="sDhwMaxTemp"></span> <span class="tempUnit"></span></pre>
</details>
</div>
</article>
@@ -508,7 +508,6 @@
setValue('.tempUnit', temperatureUnit(unitSystem));
setValue('.pressureUnit', pressureUnit(unitSystem));
setValue('.volumeUnit', volumeUnit(unitSystem));
setValue('.sAbsModMax', result.opentherm.maxModulation);
} catch (error) {
console.log(error);

View File

@@ -69,6 +69,7 @@
<option value="6" data-i18n>sensors.purposes.dhwFlowRate</option>
<option value="7" data-i18n>sensors.purposes.exhaustTemp</option>
<option value="8" data-i18n>sensors.purposes.modLevel</option>
<option value="247" data-i18n>sensors.purposes.number</option>
<option value="248" data-i18n>sensors.purposes.powerFactor</option>
<option value="249" data-i18n>sensors.purposes.power</option>
<option value="250" data-i18n>sensors.purposes.fanSpeed</option>
@@ -102,6 +103,14 @@
<option value="16" data-i18n>sensors.types.otSolarCollectorTemp</option>
<option value="17" data-i18n>sensors.types.otFanSpeedSetpoint</option>
<option value="18" data-i18n>sensors.types.otFanSpeedCurrent</option>
<option value="19" data-i18n>sensors.types.otBurnerStarts</option>
<option value="20" data-i18n>sensors.types.otDhwBurnerStarts</option>
<option value="21" data-i18n>sensors.types.otHeatingPumpStarts</option>
<option value="22" data-i18n>sensors.types.otDhwPumpStarts</option>
<option value="23" data-i18n>sensors.types.otBurnerHours</option>
<option value="24" data-i18n>sensors.types.otDhwBurnerHours</option>
<option value="25" data-i18n>sensors.types.otHeatingPumpHours</option>
<option value="26" data-i18n>sensors.types.otDhwPumpHours</option>
<option value="50" data-i18n>sensors.types.ntcTemp</option>
<option value="51" data-i18n>sensors.types.dallasTemp</option>

View File

@@ -559,6 +559,26 @@
<span data-i18n>settings.ot.options.getMinMaxTemp</span>
</label>
<label>
<input type="checkbox" name="opentherm[options][ignoreDiagState]" value="true">
<span data-i18n>settings.ot.options.ignoreDiagState</span>
</label>
<label>
<input type="checkbox" name="opentherm[options][autoFaultReset]" value="true">
<span data-i18n>settings.ot.options.autoFaultReset</span>
</label>
<label>
<input type="checkbox" name="opentherm[options][autoDiagReset]" value="true">
<span data-i18n>settings.ot.options.autoDiagReset</span>
</label>
<label>
<input type="checkbox" name="opentherm[options][setDateAndTime]" value="true">
<span data-i18n>settings.ot.options.setDateAndTime</span>
</label>
<label>
<input type="checkbox" name="opentherm[options][immergasFix]" value="true">
<span data-i18n>settings.ot.options.immergasFix</span>
@@ -981,6 +1001,10 @@
setCheckboxValue("[name='opentherm[options][dhwBlocking]']", data.opentherm.options.dhwBlocking);
setCheckboxValue("[name='opentherm[options][maxTempSyncWithTargetTemp]']", data.opentherm.options.maxTempSyncWithTargetTemp);
setCheckboxValue("[name='opentherm[options][getMinMaxTemp]']", data.opentherm.options.getMinMaxTemp);
setCheckboxValue("[name='opentherm[options][ignoreDiagState]']", data.opentherm.options.ignoreDiagState);
setCheckboxValue("[name='opentherm[options][autoFaultReset]']", data.opentherm.options.autoFaultReset);
setCheckboxValue("[name='opentherm[options][autoDiagReset]']", data.opentherm.options.autoDiagReset);
setCheckboxValue("[name='opentherm[options][setDateAndTime]']", data.opentherm.options.setDateAndTime);
setCheckboxValue("[name='opentherm[options][nativeHeatingControl]']", data.opentherm.options.nativeHeatingControl);
setCheckboxValue("[name='opentherm[options][immergasFix]']", data.opentherm.options.immergasFix);
setBusy('#ot-settings-busy', '#ot-settings', false);
@@ -1050,7 +1074,7 @@
if (data.opentherm.options.nativeHeatingControl) {
setInputValue("[name='emergency[target]']", data.emergency.target, {
"min": data.system.unitSystem == 0 ? 5 : 41,
"max": data.system.unitSystem == 0 ? 30 : 86
"max": data.system.unitSystem == 0 ? 40 : 86
});
} else {