diff --git a/Readme.md b/Readme.md index 9e76032..2f4d2ac 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,11 @@ # ESPHome OpenTherm -This is an example of a integration with a OpenTherm boiler using [ESPHome](https://esphome.io/) and [OpenTherm Adapter](http://ihormelnyk.com/opentherm_adapter) +This is an example of a integration with a OpenTherm boiler using [ESPHome](https://esphome.io/) and the [Ihor Melnyk](http://ihormelnyk.com/opentherm_adapter) or [DIYLESS](https://diyless.com/product/esp8266-thermostat-shield) OpenTherm Adapter -More details coming soon... 8-) \ No newline at end of file +## Installation +- Copy the content of this repository to your ESPHome folder +- Make sure the pin numbers are right, check the file opentherm_component.h in the esphome-opentherm folder. +- Edit the opentherm.yaml file: + - Make sure the board and device settings are correct for your device + - Set the sensor entity_id with the external temperature sensor's name from Home Assistant. (The ESPHome sensor name is temperature_sensor). +- Flash the ESP and configure in Home Assistant. It should be auto-discovered by the ESPHome Integration. diff --git a/opentherm_binary.h b/esphome-opentherm/opentherm_binary.h similarity index 100% rename from opentherm_binary.h rename to esphome-opentherm/opentherm_binary.h diff --git a/opentherm_climate.h b/esphome-opentherm/opentherm_climate.h similarity index 58% rename from opentherm_climate.h rename to esphome-opentherm/opentherm_climate.h index 596d546..688d8cd 100644 --- a/opentherm_climate.h +++ b/esphome-opentherm/opentherm_climate.h @@ -2,37 +2,49 @@ #include "esphome.h" -class OpenthermClimate : public Climate { +class OpenthermClimate : public Climate, public Component { private: const char *TAG = "opentherm_climate"; - bool supports_auto_mode_ = false; + bool supports_heat_cool_mode_ = false; bool supports_two_point_target_temperature_ = false; + float minimum_temperature_ = 0; + float maximum_temperature_ = 0; + float default_target_ = 0; public: - - void set_supports_auto_mode(bool value) - { - supports_auto_mode_ = value; - } - bool get_supports_auto_mode() - { - return supports_auto_mode_; + void set_supports_heat_cool_mode(bool value) { supports_heat_cool_mode_ = value; } + bool get_supports_heat_cool_mode() { return supports_heat_cool_mode_; } + void set_supports_two_point_target_temperature(bool value) { supports_two_point_target_temperature_ = value; } + bool get_supports_two_point_target_temperature() { return supports_two_point_target_temperature_; } + void set_temperature_settings(float min = 0, float max = 0, float target = 0) { + minimum_temperature_ = min; + maximum_temperature_ = max, + default_target_ = target; } - void set_supports_two_point_target_temperature(bool value) - { - supports_two_point_target_temperature_ = value; - } - bool get_supports_two_point_target_temperature() - { - return supports_two_point_target_temperature_; + void setup() override { + // restore setpoints + auto restore = this->restore_state_(); + if (restore.has_value()) { + restore->apply(this); + } else { + // restore from defaults + this->mode = climate::CLIMATE_MODE_OFF; + // initialize target temperature to some value so that it's not NAN + this->target_temperature = default_target_; + + if (supports_two_point_target_temperature_){ + this->target_temperature_low = minimum_temperature_; + this->target_temperature_high = maximum_temperature_; + } + } } - climate::ClimateTraits traits() override { auto traits = climate::ClimateTraits(); traits.set_supports_current_temperature(true); - traits.set_supports_auto_mode(supports_auto_mode_); + // traits.set_supports_heat_cool_mode(supports_heat_cool_mode_); + traits.set_supports_heat_cool_mode(false); traits.set_supports_cool_mode(false); traits.set_supports_heat_mode(true); traits.set_supports_two_point_target_temperature(supports_two_point_target_temperature_); @@ -91,5 +103,4 @@ class OpenthermClimate : public Climate { } } -}; - +}; \ No newline at end of file diff --git a/opentherm_component.h b/esphome-opentherm/opentherm_component.h similarity index 78% rename from opentherm_component.h rename to esphome-opentherm/opentherm_component.h index 048f030..44466ed 100644 --- a/opentherm_component.h +++ b/esphome-opentherm/opentherm_component.h @@ -6,11 +6,9 @@ #include "opentherm_binary.h" #include "opentherm_output.h" - // Pins to OpenTherm Adapter -const int inPin = D5; -const int outPin = D6; - +int inPin = D2; +int outPin = D1; OpenTherm ot(inPin, outPin, false); ICACHE_RAM_ATTR void handleInterrupt() { @@ -20,7 +18,7 @@ ICACHE_RAM_ATTR void handleInterrupt() { class OpenthermComponent: public PollingComponent { private: const char *TAG = "opentherm_component"; - OpenthermFloatOutput *pid_output_; + OpenthermFloatOutput *pid_output_; public: Switch *thermostatSwitch = new OpenthermSwitch(); Sensor *external_temperature_sensor = new Sensor(); @@ -28,14 +26,13 @@ class OpenthermComponent: public PollingComponent { Sensor *boiler_temperature = new Sensor(); Sensor *pressure_sensor = new Sensor(); Sensor *modulation_sensor = new Sensor(); - Sensor *heatting_target_temperature_sensor = new Sensor(); - Climate *hotWaterClimate = new OpenthermClimate(); + Sensor *heating_target_temperature_sensor = new Sensor(); + OpenthermClimate *hotWaterClimate = new OpenthermClimate(); OpenthermClimate *heatingWaterClimate = new OpenthermClimate(); BinarySensor *flame = new OpenthermBinarySensor(); // Set 3 sec. to give time to read all sensors (and not appear in HA as not available) - OpenthermComponent(): PollingComponent(15000) { - + OpenthermComponent(): PollingComponent(3000) { } void set_pid_output(OpenthermFloatOutput *pid_output) { pid_output_ = pid_output; } @@ -53,8 +50,13 @@ class OpenthermComponent: public PollingComponent { }); // Adjust HeatingWaterClimate depending on PID - heatingWaterClimate->set_supports_auto_mode(this->pid_output_ != nullptr); + // heatingWaterClimate->set_supports_heat_cool_mode(this->pid_output_ != nullptr); heatingWaterClimate->set_supports_two_point_target_temperature(this->pid_output_ != nullptr); + + hotWaterClimate->set_temperature_settings(5, 6, 0); + heatingWaterClimate->set_temperature_settings(0, 0, 30); + hotWaterClimate->setup(); + heatingWaterClimate->setup(); } float getExternalTemperature() { unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0)); @@ -90,7 +92,8 @@ class OpenthermComponent: public PollingComponent { void update() override { - ESP_LOGD("opentherm_component", "update"); + ESP_LOGD("opentherm_component", "update heatingWaterClimate: %i", heatingWaterClimate->mode); + ESP_LOGD("opentherm_component", "update hotWaterClimate: %i", hotWaterClimate->mode); bool enableCentralHeating = heatingWaterClimate->mode == ClimateMode::CLIMATE_MODE_HEAT; bool enableHotWater = hotWaterClimate->mode == ClimateMode::CLIMATE_MODE_HEAT; @@ -107,49 +110,46 @@ class OpenthermComponent: public PollingComponent { // Set temperature depending on room thermostat - float heatting_target_temperature; + float heating_target_temperature; if (this->pid_output_ != nullptr) { float pid_output = pid_output_->get_state(); if (pid_output == 0.0f) { - heatting_target_temperature = 10.0f; + heating_target_temperature = 10.0f; } else { - heatting_target_temperature = pid_output * (heatingWaterClimate->target_temperature_high - heatingWaterClimate->target_temperature_low) + heating_target_temperature = pid_output * (heatingWaterClimate->target_temperature_high - heatingWaterClimate->target_temperature_low) + heatingWaterClimate->target_temperature_low; } - ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (from PID Output)", heatting_target_temperature); + ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (from PID Output)", heating_target_temperature); } else if (thermostatSwitch->state) { - heatting_target_temperature = heatingWaterClimate->target_temperature; - ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (from heating water climate)", heatting_target_temperature); + heating_target_temperature = heatingWaterClimate->target_temperature; + ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (from heating water climate)", heating_target_temperature); } else { // If the room thermostat is off, set it to 10, so that the pump continues to operate - heatting_target_temperature = 10.0; - ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (default low value)", heatting_target_temperature); + heating_target_temperature = 10.0; + ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (default low value)", heating_target_temperature); } - ot.setBoilerTemperature(heatting_target_temperature); - + ot.setBoilerTemperature(heating_target_temperature); // Set hot water temperature setHotWaterTemperature(hotWaterClimate->target_temperature); - // Read sensor values - /* float boilerTemperature = ot.getBoilerTemperature(); float ext_temperature = getExternalTemperature(); float pressure = getPressure(); float modulation = getModulation(); // Publish sensor values - //flame->publish_state(isFlameOn); + flame->publish_state(isFlameOn); external_temperature_sensor->publish_state(ext_temperature); return_temperature_sensor->publish_state(return_temperature); boiler_temperature->publish_state(boilerTemperature); pressure_sensor->publish_state(pressure); modulation_sensor->publish_state(modulation); - */ - heatting_target_temperature_sensor->publish_state(heatting_target_temperature); + + heating_target_temperature_sensor->publish_state(heating_target_temperature); // Publish status of thermostat that controls hot water hotWaterClimate->current_temperature = hotWater_temperature; @@ -157,10 +157,9 @@ class OpenthermComponent: public PollingComponent { hotWaterClimate->publish_state(); // Publish status of thermostat that controls heating - heatingWaterClimate->current_temperature = return_temperature; + heatingWaterClimate->current_temperature = boilerTemperature; heatingWaterClimate->action = isCentralHeatingActive && isFlameOn ? ClimateAction::CLIMATE_ACTION_HEATING : ClimateAction::CLIMATE_ACTION_OFF; heatingWaterClimate->publish_state(); - } -}; +}; \ No newline at end of file diff --git a/opentherm_output.h b/esphome-opentherm/opentherm_output.h similarity index 100% rename from opentherm_output.h rename to esphome-opentherm/opentherm_output.h diff --git a/opentherm_switch.h b/esphome-opentherm/opentherm_switch.h similarity index 100% rename from opentherm_switch.h rename to esphome-opentherm/opentherm_switch.h diff --git a/opentherm.yaml b/opentherm.yaml index 7e8bc64..b930f8d 100644 --- a/opentherm.yaml +++ b/opentherm.yaml @@ -5,31 +5,25 @@ substitutions: esphome: name: $devicename platform: ESP8266 - board: d1_mini_lite + board: d1_mini arduino_version: '2.7.2' platformio_options: lib_deps: - - ihormelnyk/OpenTherm Library @ 1.1.0 + - ihormelnyk/OpenTherm Library @ 1.1.3 - ESP Async WebServer - includes: - - opentherm_component.h - - opentherm_climate.h - - opentherm_switch.h - - opentherm_binary.h - - opentherm_output.h + - esphome-opentherm/ wifi: - ssid: !secret wifi_name + ssid: !secret wifi_ssid password: !secret wifi_password - -# Enable logging -logger: - baud_rate: 74880 - #level: DEBUG + ap: + ssid: "${devicename} Fallback" + password: !secret esphome_fallback_password +captive_portal: +logger: api: - ota: custom_component: @@ -39,13 +33,11 @@ custom_component: components: - id: opentherm - output: - platform: custom type: float lambda: |- OpenthermComponent *openthermComp = (OpenthermComponent*) opentherm; - auto opentherm_pid_output = new OpenthermFloatOutput(); openthermComp->set_pid_output(opentherm_pid_output); App.register_component(opentherm_pid_output); @@ -65,30 +57,33 @@ sensor: openthermComp->return_temperature_sensor, openthermComp->pressure_sensor, openthermComp->modulation_sensor, - openthermComp->heatting_target_temperature_sensor + openthermComp->heating_target_temperature_sensor }; sensors: - - name: "Boiler Temperature" + - name: Boiler Temperature unit_of_measurement: °C - accuracy_decimals: 2 + accuracy_decimals: 1 - name: "External Temperature" unit_of_measurement: °C accuracy_decimals: 0 - name: "Return Temperature" unit_of_measurement: °C - accuracy_decimals: 2 + accuracy_decimals: 1 - name: "Heating Water Pressure" unit_of_measurement: hPa - accuracy_decimals: 2 + accuracy_decimals: 1 - name: "Boiler Modulation" unit_of_measurement: "%" accuracy_decimals: 0 - name: "Heating Target Temperature" unit_of_measurement: ºC - accuracy_decimals: 2 + accuracy_decimals: 1 + - platform: homeassistant id: temperature_sensor entity_id: sensor.temperature_sensor + name: "Actual temperature" + unit_of_measurement: °C - platform: pid name: "PID Climate Result" type: RESULT @@ -104,7 +99,6 @@ binary_sensor: lambda: |- OpenthermComponent *openthermComp = (OpenthermComponent*) opentherm; return {openthermComp->flame}; - binary_sensors: - name: "Flame" #device_class: heat @@ -114,9 +108,8 @@ switch: lambda: |- OpenthermComponent *openthermComp = (OpenthermComponent*) opentherm; return {openthermComp->thermostatSwitch}; - switches: - name: "Termostato ambiente" + name: "Disable PID" - platform: template name: "PID Climate Autotune" turn_on_action: @@ -130,17 +123,22 @@ climate: openthermComp->hotWaterClimate, openthermComp->heatingWaterClimate }; - climates: - - name: "Hot water" - - name: "Heating water" + - id: hot_water + name: "Hot water" + - id: heating_water + name: "Heating water" - platform: pid id: pid_climate name: "PID Climate Controller" + visual: + min_temperature: 16 °C + max_temperature: 28 °C + temperature_step: 0.5 °C sensor: temperature_sensor - default_target_temperature: 21°C + default_target_temperature: 20°C heat_output: pid_output control_parameters: kp: 0.38197 ki: 0.01012 - kd: 3.60387 + kd: 3.60387 \ No newline at end of file