The Good:
After a lot of planning, searching, and HELP from a lot of people I now consider friends for putting up with my requests/needs and for their time spent helping me, I present to you a very ugly but simple and efficient way to control the heating in one’s house or even multi apartment block, if one needs.
I guess this will be the most uninteresting post picture-wise:
There is also a third thermostat for the 3rd room.
This little card can be omitted, I just got some info displayed to be sure everything is working, and to check the status of some sensors.
What I wanted to share is the required coding needed in configuration.yaml . As this was very hard for me to do, and I hope that it will help other people looking to heat their homes more efficiently and more comfortably, without spending hundreds of dollars/euros or even more for this.
This project will allow one to control the heating temperature, heating on/off, enable/disable heating based on window being close/open per each zone or room, while controlling only one opentherm boiler, and allowing it to work as efficiently as possible.
The Bad:
Of course, the bad thing here (although a lot better than buying a ready system) is the cost.
You will need:
a. an opentherm adapter shield. Designed by Ihor, from http://ihormelnyk.com/ , the latest version can be bought from https://diyless.com/ .
20 euros shipped.
b. any kind of esp computer. nodemcu, d1 mini, esp32, ANYTHING. I used a nodemcu. I draw power from the boiler’s main unit (230VAC) and power the nodemcu through a tiny 230vac-5vcc psu rom aliexpress.
3+2 euros.
c. one zigbee or wifi TRV for each radiator you want to control. For areas wih multiple radiators, check if you have any kind of general distributor, which will save you a lot of money! Usually each room has a separate line from the main distributor.
I used saswell 801 zigbee from aliexpress
25 euros per piece, 75 euros for my 3 rooms.
d. window/door sensor. Any. xiaomi are best represented on aliexpress.
10 euros per piece, 30 euros for my 3 rooms.
e. special radiator/distributor valves, that are able to be controlled with TRVs. They should cost around ~10 euros a piece.
10 euros per piece, 30 euros for my 3 rooms.
f. zigbee coordinator, raspberry pi if you do not have a home assistant instance already, power supply, sd card…
50 euro.
TOTAL: 160 euro for me. instead of who knows how much… I already have a sevrer with a vm running HA.
The ugly:
Configuration.yaml:
climate:
#
- platform: mqtt
modes:
- "off"
- "heat"
name: Dormitor HVAC
current_temperature_topic: "HVAC/dormitor/ct"
mode_command_topic: "HVAC/dormitor/mode/set"
temperature_command_topic: "HVAC/dormitor/st"
min_temp: 12
max_temp: 26
value_template: "{{ value }}"
temp_step: 0.5
#
- platform: mqtt
modes:
- "off"
- "heat"
name: Copii HVAC
current_temperature_topic: "HVAC/copii/ct"
mode_command_topic: "HVAC/copii/mode/set"
temperature_command_topic: "HVAC/copii/st"
min_temp: 12
max_temp: 26
value_template: "{{ value }}"
temp_step: 0.5
#
- platform: mqtt
modes:
- "off"
- "heat"
name: Sufragerie HVAC
current_temperature_topic: "HVAC/sufragerie/ct"
mode_command_topic: "HVAC/dormitor/sufragerie/set"
temperature_command_topic: "HVAC/sufragerie/st"
min_temp: 12
max_temp: 26
value_template: "{{ value }}"
temp_step: 0.5
#
binary_sensor:
#
- platform: mqtt
name: copii_geam
state_topic: 'zigbee2mqtt/Copii_geam'
value_template: "{{ value_json.contact }}"
payload_on: false
payload_off: true
qos: 0
#
- platform: mqtt
name: dormitor_geam
state_topic: 'zigbee2mqtt/Dormitor_geam'
value_template: "{{ value_json.contact }}"
payload_on: false
payload_off: true
qos: 0
#
- platform: mqtt
name: sufragerie_geam
state_topic: 'zigbee2mqtt/Sufragerie_geam'
value_template: "{{ value_json.contact }}"
payload_on: false
payload_off: true
qos: 0
#
One will have to make a thermostat integration for each needed zone that he wants controlled and a binary sensor (if needed, depending on the devices’ integration) for each window sensor.
automations.yaml:
- alias: temp_dormitor
trigger:
- platform: state
entity_id: sensor.0x00158d000465809a_temperature
action:
service: mqtt.publish
data:
payload: "{{ states('sensor.0x00158d000465809a_temperature') }}"
topic: "HVAC/dormitor/ct"
qos: 0
retain: true
#
- alias: temp_copii
trigger:
- platform: state
entity_id: sensor.0x00158d000464b329_temperature
action:
service: mqtt.publish
data:
payload: "{{ states('sensor.0x00158d000464b329_temperature') }}"
topic: "HVAC/copii/ct"
qos: 0
retain: true
#
- alias: temp_sufragerie
trigger:
- platform: state
entity_id: sensor.0x00158d00045d01f4_temperature
action:
service: mqtt.publish
data:
payload: "{{ states('sensor.0x00158d00045d01f4_temperature') }}"
topic: "HVAC/sufragerie/ct"
qos: 0
retain: true
#
- alias: trv_copii
trigger:
- platform: state
entity_id: climate.copii_hvac, sensor.0x00158d000464b329_temperature, binary_sensor.copii_geam
action:
- service: mqtt.publish
data:
payload_template: >-
{% set targetTemp = state_attr('climate.copii_hvac', 'temperature') %}
{% set currentTemp = states('sensor.0x00158d000464b329_temperature') %}
{% set window = states('binary_sensor.copii_geam') %}
{% set mode = states('climate.copii_hvac') %}
{% if targetTemp == None or currentTemp == None or currentTemp == 'unknown' or window == None and mode == 'heat' %}
{{ '{"system_mode": "heat"}' }}
{% else %}
{% if targetTemp|float > currentTemp|float and window == 'off' and mode == 'heat' %}
{{ '{"system_mode": "heat"}' }}
{% else %}
{{ '{"system_mode": "off"}' }}
{% endif %}
{% endif %}
topic: "zigbee2mqtt/Copii/set"
- event: FIRE_OT_RECALC_EVENT
#
- alias: trv_dormitor
trigger:
- platform: state
entity_id: climate.dormitor_hvac, sensor.0x00158d000465809a_temperature, binary_sensor.dormitor_geam
action:
- service: mqtt.publish
data:
payload_template: >-
{% set targetTemp = state_attr('climate.dormitor_hvac', 'temperature') %}
{% set currentTemp = states('sensor.0x00158d000465809a_temperature') %}
{% set window = states('binary_sensor.dormitor_geam') %}
{% set mode = states('climate.dormitor_hvac') %}
{% if targetTemp == None or currentTemp == None or currentTemp == 'unknown' or window == None and mode == 'heat' %}
{{ '{"system_mode": "heat"}' }}
{% else %}
{% if targetTemp|float > currentTemp|float and window == 'off' and mode == 'heat' %}
{{ '{"system_mode": "heat"}' }}
{% else %}
{{ '{"system_mode": "off"}' }}
{% endif %}
{% endif %}
topic: "zigbee2mqtt/Dormitor/set"
- event: FIRE_OT_RECALC_EVENT
#
- alias: trv_sufragerie
trigger:
- platform: state
entity_id: climate.sufragerie_hvac, sensor.0x00158d00045d01f4_temperature, binary_sensor.sufragerie_geam
action:
- service: mqtt.publish
data:
payload_template: >-
{% set targetTemp = state_attr('climate.sufragerie_hvac', 'temperature') %}
{% set currentTemp = states('sensor.0x00158d00045d01f4_temperature') %}
{% set window = states('binary_sensor.sufragerie_geam') %}
{% set mode = states('climate.sufragerie_hvac') %}
{% if targetTemp == None or currentTemp == None or currentTemp == 'unknown' or window == None and mode == 'heat' %}
{{ '{"system_mode": "heat"}' }}
{% else %}
{% if targetTemp|float > currentTemp|float and window == 'off' and mode == 'heat' %}
{{ '{"system_mode": "heat"}' }}
{% else %}
{{ '{"system_mode": "off"}' }}
{% endif %}
{% endif %}
topic: "zigbee2mqtt/Sufragerie/set"
- event: FIRE_OT_RECALC_EVENT
#
- alias: OpenTherm Calculation
trigger:
- platform: event
event_type: FIRE_OT_RECALC_EVENT
action:
- service: mqtt.publish
data:
payload_template: >-
{% set targetTemp1 = state_attr('climate.dormitor_hvac', 'temperature') %}
{% set currentTemp1 = states('sensor.0x00158d000465809a_temperature') %}
{% set window1 = states('binary_sensor.dormitor_geam') %}
{% set mode1 = states('climate.dormitor_hvac') %}
{% set targetTemp2 = state_attr('climate.copii_hvac', 'temperature') %}
{% set currentTemp2 = states('sensor.0x00158d000464b329_temperature') %}
{% set window2 = states('binary_sensor.copii_geam') %}
{% set mode2 = states('climate.copii_hvac') %}
{% set targetTemp3 = state_attr('climate.sufragerie_hvac', 'temperature') %}
{% set currentTemp3 = states('sensor.0x00158d00045d01f4_temperature') %}
{% set window3 = states('binary_sensor.sufragerie_geam') %}
{% set mode3 = states('climate.sufragerie_hvac') %}
{% if targetTemp1|float > currentTemp1|float and window1 == 'off' and mode1 == 'heat' %}
{% set result1 = targetTemp1 %}
{% else %}
{% set result1 = '30' %}
{% endif %}
{% if targetTemp2|float > currentTemp2|float and window2 == 'off' and mode2 == 'heat' %}
{% set result2 = targetTemp2 %}
{% else %}
{% set result2 = '30' %}
{% endif %}
{% if targetTemp3|float > currentTemp3|float and mode3 == 'heat' %}
{% set result3 = targetTemp3 %}
{% else %}
{% set result3 = '30' %}
{% endif %}
{% if result1 == [result1|float, result2|float, result3|float]|min and result1 < 30 %}
{% set result = (targetTemp1,currentTemp1|float,'heat') %}
{% else %}
{% if result2 == [result1|float, result2|float, result3|float]|min and result2 < 30 %}
{% set result = (targetTemp2,currentTemp2|float,'heat') %}
{% else %}
{% if result3 == [result1|float, result2|float, result3|float]|min and result3 < 30 %}
{% set result = (targetTemp3,currentTemp3|float,'heat') %}
{% else %}
{% set result = (1.0, 20.0, 'off') %}
{% endif%}
{% endif %}
{% endif %}
{{ result }}
topic: "HVAC/otresult"
retain: true
#
- alias: OpenTherm Force Recalc
trigger:
- platform: time_pattern
seconds: "/20"
action:
- event: FIRE_OT_RECALC_EVENT
#
- alias: OpenTherm Send
trigger:
- platform: mqtt
topic: "HVAC/otresult"
action:
- service: mqtt.publish
data:
payload_template: "{{ trigger.payload.split(',')[0][1:] }}"
topic: "opentherm-thermostat/setpoint-temperature/set"
retain: true
qos: 0
- service: mqtt.publish
data:
payload_template: "{{ trigger.payload.split(',')[1][:-1:] }}"
topic: "opentherm-thermostat/current-temperature/set"
retain: true
qos: 0
- service: mqtt.publish
data:
payload_template: >
{{ trigger.payload.split(',')[2][:-1] | replace("'","") }}
topic: "opentherm-thermostat/mode/set"
retain: true
qos: 0
#
One will have to have a temperature sensor for each zone/room he needs to control, and this value must be published to a topic, as you can see in the first 3 automations.
One will have to have an automation for each of the TRVs that he wants to control. As I have 3 TRVs on my radiators, I have 3 automations, that will open the TRV in case there is any error in the messages regarding temperature or window and the mode of that area is set to heat; it will open if the mode is set to heating and current temperature is lower than set temperature; and t will close in case heating for that area is set to off or the room temperature is higher than set temperature.
The next automation is taking care that the opentherm boiler is only working when needed (any one of the areas/rooms needs hear e.g. set temp>current temp) and is always taking into consideration the room that needs the lowest set temperature, as this will make the boiler run most efficient for a condensing boiler unit.
The rest is just a way to publish the required data to the required mqtt topics.
This can be used to learn a lot about forwarding mqtt messages or parts of one dictionary. This gave me a big headache.