From 5de3238f6f69a850f9468d93e80453ec47a99aab Mon Sep 17 00:00:00 2001 From: Yurii Date: Mon, 9 Oct 2023 06:20:55 +0300 Subject: [PATCH] Added DHW present switch --- .../WiFiManagerParameters.h | 38 ++++++++++ src/HomeAssistantHelper.h | 36 ++++++++++ src/MqttTask.h | 41 +++++++---- src/OpenThermTask.h | 66 +++++++++-------- src/Settings.h | 1 + src/WifiManagerTask.h | 72 +++++++++++-------- 6 files changed, 182 insertions(+), 72 deletions(-) create mode 100644 lib/WiFiManagerParameters/WiFiManagerParameters.h diff --git a/lib/WiFiManagerParameters/WiFiManagerParameters.h b/lib/WiFiManagerParameters/WiFiManagerParameters.h new file mode 100644 index 0000000..23ab14d --- /dev/null +++ b/lib/WiFiManagerParameters/WiFiManagerParameters.h @@ -0,0 +1,38 @@ +class IntParameter : public WiFiManagerParameter { +public: + IntParameter(const char* id, const char* label, int value, const uint8_t length = 10) : WiFiManagerParameter("") { + init(id, label, String(value).c_str(), length, "", WFM_LABEL_DEFAULT); + } + + int getValue() { + return atoi(WiFiManagerParameter::getValue()); + } +}; + +class CheckboxParameter : public WiFiManagerParameter { +public: + const char* checked = "type=\"checkbox\" checked"; + const char* noChecked = "type=\"checkbox\""; + const char* trueVal = "T"; + + CheckboxParameter(const char* id, const char* label, bool value): WiFiManagerParameter("") { + init(id, label, value ? trueVal : "0", 1, "", WFM_LABEL_AFTER); + } + + const char* getValue() const override { + return trueVal; + } + + const char* getCustomHTML() const override { + return strcmp(WiFiManagerParameter::getValue(), trueVal) == 0 ? checked : noChecked; + } + + bool getCheckboxValue() { + return strcmp(WiFiManagerParameter::getValue(), trueVal) == 0 ? true : false; + } +}; + +class SeparatorParameter : public WiFiManagerParameter { +public: + SeparatorParameter(): WiFiManagerParameter("
") {} +}; \ No newline at end of file diff --git a/src/HomeAssistantHelper.h b/src/HomeAssistantHelper.h index d843a29..bcff04f 100644 --- a/src/HomeAssistantHelper.h +++ b/src/HomeAssistantHelper.h @@ -1807,6 +1807,42 @@ public: return client.publish((F("homeassistant/sensor/") + _prefix + F("/indoor_temp/config")).c_str(), NULL, true); } + bool deleteSwitchDHW() { + return client.publish((F("homeassistant/switch/") + _prefix + F("/dhw/config")).c_str(), NULL, true); + } + + bool deleteSensorCurrentDHWMinTemp() { + return client.publish((F("homeassistant/sensor/") + _prefix + F("/current_dhw_min_temp/config")).c_str(), NULL, true); + } + + bool deleteSensorCurrentDHWMaxTemp() { + return client.publish((F("homeassistant/sensor/") + _prefix + F("/current_dhw_max_temp/config")).c_str(), NULL, true); + } + + bool deleteNumberDHWMinTemp() { + return client.publish((F("homeassistant/number/") + _prefix + F("/dhw_min_temp/config")).c_str(), NULL, true); + } + + bool deleteNumberDHWMaxTemp() { + return client.publish((F("homeassistant/number/") + _prefix + F("/dhw_max_temp/config")).c_str(), NULL, true); + } + + bool deleteBinSensorDHW() { + return client.publish((F("homeassistant/binary_sensor/") + _prefix + F("/dhw/config")).c_str(), NULL, true); + } + + bool deleteSensorDHWTemp() { + return client.publish((F("homeassistant/sensor/") + _prefix + F("/dhw_temp/config")).c_str(), NULL, true); + } + + bool deleteNumberDHWTarget() { + return client.publish((F("homeassistant/number/") + _prefix + F("/dhw_target/config")).c_str(), NULL, true); + } + + bool deleteClimateDHW() { + return client.publish((F("homeassistant/climate/") + _prefix + F("_dhw/config")).c_str(), NULL, true); + } + private: String _prefix = "opentherm"; String _deviceVersion = "1.0"; diff --git a/src/MqttTask.h b/src/MqttTask.h index 5f6d595..ba42fd7 100644 --- a/src/MqttTask.h +++ b/src/MqttTask.h @@ -394,14 +394,6 @@ protected: haHelper.publishNumberHeatingMinTemp(false); haHelper.publishNumberHeatingMaxTemp(false); - // dhw - haHelper.publishSwitchDHW(false); - //haHelper.publishNumberDHWTarget(false); - haHelper.publishSensorCurrentDHWMinTemp(false); - haHelper.publishSensorCurrentDHWMaxTemp(false); - haHelper.publishNumberDHWMinTemp(false); - haHelper.publishNumberDHWMaxTemp(false); - // pid haHelper.publishSwitchPID(); haHelper.publishNumberPIDFactorP(); @@ -424,7 +416,6 @@ protected: haHelper.publishBinSensorStatus(); haHelper.publishBinSensorOtStatus(); haHelper.publishBinSensorHeating(); - haHelper.publishBinSensorDHW(); haHelper.publishBinSensorFlame(); haHelper.publishBinSensorFault(); haHelper.publishBinSensorDiagnostic(); @@ -439,12 +430,11 @@ protected: haHelper.publishNumberIndoorTemp(); //haHelper.publishNumberOutdoorTemp(); haHelper.publishSensorHeatingTemp(); - haHelper.publishSensorDHWTemp(); } static bool publishNonStaticHaEntities(bool force = false) { static byte _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp; - static bool _editableOutdoorTemp, _editableIndoorTemp; + static bool _editableOutdoorTemp, _editableIndoorTemp, _dhwPresent; bool published = false; bool isStupidMode = !settings.pid.enable && !settings.equitherm.enable; @@ -453,6 +443,33 @@ protected: bool editableOutdoorTemp = settings.sensors.outdoor.type == 1; bool editableIndoorTemp = settings.sensors.indoor.type == 1; + if (force || _dhwPresent != settings.opentherm.dhwPresent) { + _dhwPresent = settings.opentherm.dhwPresent; + + if (_dhwPresent) { + haHelper.publishSwitchDHW(false); + haHelper.publishSensorCurrentDHWMinTemp(false); + haHelper.publishSensorCurrentDHWMaxTemp(false); + haHelper.publishNumberDHWMinTemp(false); + haHelper.publishNumberDHWMaxTemp(false); + haHelper.publishBinSensorDHW(); + haHelper.publishSensorDHWTemp(); + + } else { + haHelper.deleteSwitchDHW(); + haHelper.deleteSensorCurrentDHWMinTemp(); + haHelper.deleteSensorCurrentDHWMaxTemp(); + haHelper.deleteNumberDHWMinTemp(); + haHelper.deleteNumberDHWMaxTemp(); + haHelper.deleteBinSensorDHW(); + haHelper.deleteSensorDHWTemp(); + haHelper.deleteNumberDHWTarget(); + haHelper.deleteClimateDHW(); + } + + published = true; + } + if (force || _heatingMinTemp != heatingMinTemp || _heatingMaxTemp != heatingMaxTemp) { if (settings.heating.target < heatingMinTemp || settings.heating.target > heatingMaxTemp) { settings.heating.target = constrain(settings.heating.target, heatingMinTemp, heatingMaxTemp); @@ -467,7 +484,7 @@ protected: published = true; } - if (force || _dhwMinTemp != vars.parameters.dhwMinTemp || _dhwMaxTemp != vars.parameters.dhwMaxTemp) { + if (_dhwPresent && (force || _dhwMinTemp != vars.parameters.dhwMinTemp || _dhwMaxTemp != vars.parameters.dhwMaxTemp)) { _dhwMinTemp = vars.parameters.dhwMinTemp; _dhwMaxTemp = vars.parameters.dhwMaxTemp; diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index f7c7eb0..49a9c86 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -36,25 +36,30 @@ protected: WARN("Slave member id failed"); } - bool heatingEnable = (vars.states.emergency || settings.heating.enable) && pump && isReady(); + bool _heatingEnabled = (vars.states.emergency || settings.heating.enable) && pump && isReady(); localResponse = ot->setBoilerStatus( - heatingEnable, - settings.dhw.enable, + _heatingEnabled, + settings.opentherm.dhwPresent && settings.dhw.enable, false, false, true, false, false ); + if (!ot->isValidResponse(localResponse)) { WARN_F("Invalid response after setBoilerStatus: %s\r\n", ot->statusToString(ot->getLastResponseStatus())); return; } - INFO_F("Heating enabled: %d\r\n", heatingEnable); - setMaxModulationLevel(heatingEnable ? 100 : 0); + if ( heatingEnabled != _heatingEnabled ) { + heatingEnabled = _heatingEnabled; + INFO_F("Heating enabled: %s\r\n", heatingEnabled ? "on\0" : "off\0"); + } vars.states.heating = ot->isCentralHeatingActive(localResponse); - vars.states.dhw = ot->isHotWaterActive(localResponse); + vars.states.dhw = settings.opentherm.dhwPresent ? ot->isHotWaterActive(localResponse) : false; vars.states.flame = ot->isFlameOn(localResponse); vars.states.fault = ot->isFault(localResponse); vars.states.diagnostic = ot->isDiagnostic(localResponse); + + setMaxModulationLevel(heatingEnabled ? 100 : 0); yield(); // Команды чтения данных котла @@ -65,7 +70,9 @@ protected: DEBUG_F("Master type: %u, version: %u\r\n", vars.parameters.masterType, vars.parameters.masterVersion); DEBUG_F("Slave type: %u, version: %u\r\n", vars.parameters.slaveType, vars.parameters.slaveVersion); - updateMinMaxDhwTemp(); + if ( settings.opentherm.dhwPresent ) { + updateMinMaxDhwTemp(); + } updateMinMaxHeatingTemp(); if (settings.sensors.outdoor.type == 0) { @@ -85,28 +92,28 @@ protected: } updatePressure(); - if ( settings.dhw.enable || settings.heating.enable || heatingEnable ) { + if ((settings.opentherm.dhwPresent && settings.dhw.enable) || settings.heating.enable || heatingEnabled ) { updateModulationLevel(); } yield(); - if ( settings.dhw.enable ) { + if ( settings.opentherm.dhwPresent && settings.dhw.enable ) { updateDHWTemp(); } else { vars.temperatures.dhw = 0; } - if ( settings.heating.enable || heatingEnable ) { + //if ( settings.heating.enable || heatingEnabled ) { updateHeatingTemp(); - } else { - vars.temperatures.heating = 0; - } + //} else { + // vars.temperatures.heating = 0; + //} yield(); // // Температура ГВС byte newDHWTemp = settings.dhw.target; - if (settings.dhw.enable && newDHWTemp != currentDHWTemp) { + if (settings.opentherm.dhwPresent && settings.dhw.enable && newDHWTemp != currentDHWTemp) { if (newDHWTemp < vars.parameters.dhwMinTemp || newDHWTemp > vars.parameters.dhwMaxTemp) { newDHWTemp = constrain(newDHWTemp, vars.parameters.dhwMinTemp, vars.parameters.dhwMaxTemp); } @@ -124,7 +131,7 @@ protected: // // Температура отопления - if (heatingEnable && fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) { + if (heatingEnabled && fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) { INFO_F("Setting heating temp = %u \n", vars.parameters.heatingSetpoint); // Записываем заданную температуру @@ -194,6 +201,7 @@ protected: protected: bool pump = true; + bool heatingEnabled = false; unsigned long prevUpdateNonEssentialVars = 0; unsigned long startupTime = millis(); @@ -224,23 +232,23 @@ protected: //======================================================================================= unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0)); // 0xFFFF - /*uint8_t flags = (response & 0xFFFF) >> 8; - DEBUG_F( - "MasterMemberIdCode:\r\n DHW present: %u\r\n Control type: %u\r\n Cooling configuration: %u\r\n DHW configuration: %u\r\n Pump control: %u\r\n CH2 present: %u\r\n Remote water filling function: %u\r\n Heat/cool mode control: %u\r\n Slave MemberID Code: %u\r\n", - flags & 0x01, - flags & 0x02, - flags & 0x04, - flags & 0x08, - flags & 0x10, - flags & 0x20, - flags & 0x40, - flags & 0x80, - response & 0xFF - );*/ - if (ot->isValidResponse(response)) { vars.parameters.slaveMemberIdCode = response & 0xFF; + /*uint8_t flags = (response & 0xFFFF) >> 8; + DEBUG_F( + "MasterMemberIdCode:\r\n DHW present: %u\r\n Control type: %u\r\n Cooling configuration: %u\r\n DHW configuration: %u\r\n Pump control: %u\r\n CH2 present: %u\r\n Remote water filling function: %u\r\n Heat/cool mode control: %u\r\n Slave MemberID Code: %u\r\n", + flags & 0x01, + flags & 0x02, + flags & 0x04, + flags & 0x08, + flags & 0x10, + flags & 0x20, + flags & 0x40, + flags & 0x80, + response & 0xFF + );*/ + } else if ( settings.opentherm.memberIdCode <= 0 ) { return false; } diff --git a/src/Settings.h b/src/Settings.h index 1b3de40..58ab545 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -6,6 +6,7 @@ struct Settings { byte inPin = 4; byte outPin = 5; unsigned int memberIdCode = 0; + bool dhwPresent = true; } opentherm; struct { diff --git a/src/WifiManagerTask.h b/src/WifiManagerTask.h index 950853e..490f717 100644 --- a/src/WifiManagerTask.h +++ b/src/WifiManagerTask.h @@ -1,23 +1,28 @@ #include +#include // Wifimanager WiFiManager wm; WiFiManagerParameter* wmHostname; WiFiManagerParameter* wmMqttServer; -WiFiManagerParameter* wmMqttPort; +IntParameter* wmMqttPort; WiFiManagerParameter* wmMqttUser; WiFiManagerParameter* wmMqttPassword; WiFiManagerParameter* wmMqttPrefix; -WiFiManagerParameter* wmMqttPublishInterval; -WiFiManagerParameter* wmOtInPin; -WiFiManagerParameter* wmOtOutPin; -WiFiManagerParameter* wmOtMemberIdCode; -WiFiManagerParameter* wmOutdoorSensorPin; -WiFiManagerParameter* wmIndoorSensorPin; +IntParameter* wmMqttPublishInterval; +IntParameter* wmOtInPin; +IntParameter* wmOtOutPin; +IntParameter* wmOtMemberIdCode; +CheckboxParameter* wmOtDHWPresent; +IntParameter* wmOutdoorSensorPin; +IntParameter* wmIndoorSensorPin; -class WifiManagerTask: public Task { +SeparatorParameter* wmSep1; +SeparatorParameter* wmSep2; + +class WifiManagerTask : public Task { public: - WifiManagerTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {} + WifiManagerTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {} protected: void setup() { @@ -30,41 +35,43 @@ protected: wmMqttServer = new WiFiManagerParameter("mqtt_server", "MQTT server", settings.mqtt.server, 80); wm.addParameter(wmMqttServer); - sprintf(buffer, "%d", settings.mqtt.port); - wmMqttPort = new WiFiManagerParameter("mqtt_port", "MQTT port", buffer, 6); + wmMqttPort = new IntParameter("mqtt_port", "MQTT port", settings.mqtt.port, 6); wm.addParameter(wmMqttPort); wmMqttUser = new WiFiManagerParameter("mqtt_user", "MQTT username", settings.mqtt.user, 32); wm.addParameter(wmMqttUser); - wmMqttPassword = new WiFiManagerParameter("mqtt_password", "MQTT password", settings.mqtt.password, 32); + wmMqttPassword = new WiFiManagerParameter("mqtt_password", "MQTT password", settings.mqtt.password, 32, "type=\"password\""); wm.addParameter(wmMqttPassword); wmMqttPrefix = new WiFiManagerParameter("mqtt_prefix", "MQTT prefix", settings.mqtt.prefix, 32); wm.addParameter(wmMqttPrefix); - sprintf(buffer, "%d", settings.mqtt.interval); - wmMqttPublishInterval = new WiFiManagerParameter("mqtt_publish_interval", "MQTT publish interval", buffer, 5); + wmMqttPublishInterval = new IntParameter("mqtt_publish_interval", "MQTT publish interval", settings.mqtt.interval, 5); wm.addParameter(wmMqttPublishInterval); - sprintf(buffer, "%d", settings.opentherm.inPin); - wmOtInPin = new WiFiManagerParameter("ot_in_pin", "Opentherm pin IN", buffer, 2); + wmSep1 = new SeparatorParameter(); + wm.addParameter(wmSep1); + + wmOtInPin = new IntParameter("ot_in_pin", "Opentherm pin IN", settings.opentherm.inPin, 2); wm.addParameter(wmOtInPin); - sprintf(buffer, "%d", settings.opentherm.outPin); - wmOtOutPin = new WiFiManagerParameter("ot_out_pin", "Opentherm pin OUT", buffer, 2); + wmOtOutPin = new IntParameter("ot_out_pin", "Opentherm pin OUT", settings.opentherm.outPin, 2); wm.addParameter(wmOtOutPin); - sprintf(buffer, "%d", settings.opentherm.memberIdCode); - wmOtMemberIdCode = new WiFiManagerParameter("ot_member_id_code", "Opentherm member id", buffer, 5); + wmOtMemberIdCode = new IntParameter("ot_member_id_code", "Opentherm member id", settings.opentherm.memberIdCode, 5); wm.addParameter(wmOtMemberIdCode); - sprintf(buffer, "%d", settings.sensors.outdoor.pin); - wmOutdoorSensorPin = new WiFiManagerParameter("outdoor_sensor_pin", "Outdoor sensor pin", buffer, 2); + wmOtDHWPresent = new CheckboxParameter("ot_dhw_present", "Opentherm DHW present", settings.opentherm.dhwPresent); + wm.addParameter(wmOtDHWPresent); + + wmSep2 = new SeparatorParameter(); + wm.addParameter(wmSep2); + + wmOutdoorSensorPin = new IntParameter("outdoor_sensor_pin", "Outdoor sensor pin", settings.sensors.outdoor.pin, 2); wm.addParameter(wmOutdoorSensorPin); - sprintf(buffer, "%d", settings.sensors.indoor.pin); - wmIndoorSensorPin = new WiFiManagerParameter("indoor_sensor_pin", "Indoor sensor pin", buffer, 2); + wmIndoorSensorPin = new IntParameter("indoor_sensor_pin", "Indoor sensor pin", settings.sensors.indoor.pin, 2); wm.addParameter(wmIndoorSensorPin); //wm.setCleanConnect(true); @@ -110,16 +117,17 @@ protected: void static saveParamsCallback() { strcpy(settings.hostname, wmHostname->getValue()); strcpy(settings.mqtt.server, wmMqttServer->getValue()); - settings.mqtt.port = atoi(wmMqttPort->getValue()); + settings.mqtt.port = wmMqttPort->getValue(); strcpy(settings.mqtt.user, wmMqttUser->getValue()); strcpy(settings.mqtt.password, wmMqttPassword->getValue()); strcpy(settings.mqtt.prefix, wmMqttPrefix->getValue()); - settings.mqtt.interval = atoi(wmMqttPublishInterval->getValue()); - settings.opentherm.inPin = atoi(wmOtInPin->getValue()); - settings.opentherm.outPin = atoi(wmOtOutPin->getValue()); - settings.opentherm.memberIdCode = atoi(wmOtMemberIdCode->getValue()); - settings.sensors.outdoor.pin = atoi(wmOutdoorSensorPin->getValue()); - settings.sensors.indoor.pin = atoi(wmIndoorSensorPin->getValue()); + settings.mqtt.interval = wmMqttPublishInterval->getValue(); + settings.opentherm.inPin = wmOtInPin->getValue(); + settings.opentherm.outPin = wmOtOutPin->getValue(); + settings.opentherm.memberIdCode = wmOtMemberIdCode->getValue(); + settings.opentherm.dhwPresent = wmOtDHWPresent->getCheckboxValue(); + settings.sensors.outdoor.pin = wmOutdoorSensorPin->getValue(); + settings.sensors.indoor.pin = wmIndoorSensorPin->getValue(); INFO_F( "New settings:\r\n" @@ -132,6 +140,7 @@ protected: " OT in pin: %d\r\n" " OT out pin: %d\r\n" " OT member id code: %d\r\n" + " OT DHW present: %d\r\n" " Outdoor sensor pin: %d\r\n" " Indoor sensor pin: %d\r\n", settings.hostname, @@ -144,6 +153,7 @@ protected: settings.opentherm.inPin, settings.opentherm.outPin, settings.opentherm.memberIdCode, + settings.opentherm.dhwPresent, settings.sensors.outdoor.pin, settings.sensors.indoor.pin );