Rather Advance Logic Automation (Water Tanks and Solar Energy)

Hi there folks,

I have recently migrated my Smart Home from smarthomeNG to Home Assistant. And I really love HA for what it is. Easy and maintainable. All can be done from the UI, which is really nice.

There is just one feature I cannot wrap my head around on how to solve that in HA.

In smarthomeNG you can use a python script to run a logic and trigger events.

For example I did this:

from datetime import datetime, time
now = datetime.now()
now_time = now.time()

TWO = sh.Zentral.Kesseltemperaturen.TrinkwasserOben() # Read one temperature
HWO = sh.Zentral.Kesseltemperaturen.HeizungswasserOben() # Read another temperature
Pumpe = sh.Zentral.Heizung.Pumpe() # Read the pump status
Automatik = sh.Zentral.Heizung.Automatik() # Check if the variable automatik is True

if now_time >= time(9,00) and now_time <= time(20,00):
 Nacht = False;
else:
 Nacht = True;

if Automatik == True and Nacht == False:
 if (TWO < 40 or TWO > 60) and (TWO > HWO+5 or TWO > 70):
  if Pumpe == False:
   sh.Zentral.Heizung.Pumpe(True) # activate pump

 elif (HWO > 50 and TWO > 45) and (TWO < 55 or TWO+5 > HWO):
  if Pumpe == True:
   sh.Zentral.Heizung.Pumpe(False) # deactivate pump
  
 else:
  if Pumpe == True:
   sh.Zentral.Heizung.Pumpe(False) # deactivate pump

else:
 if Nacht == True and Automatik == True:
  if Pumpe == True:
   sh.Zentral.Heizung.Pumpe(False) # deactivate pump

It does compare two temperature readings and a status of a pump and in addition a time frame and then decide whether the pump should be activated or not.

I desperately tried to do that in HA but did not succeed. I tried using the basic python implementation and I tried using templates.

This is my last template approach to try and see if it works:

template:
  - binary_sensor:
    - name: "Heizungsautomatik Entscheidung"
      unique_id: heizungsautomatik_entscheidung
      state: >
        {% set two = states('sensor.28_ffd7b0821603_temperatur') | float(0) %}
        {% set hwo = states('sensor.28_ffbd5b831603_temperatur') | float(0) %}
        {% set pump = is_state('switch.heizung_pumpe', 'on') %}
        
        {% if (two < 40 or two > 60) and (two > hwo + 5 or two > 70) %}
          {{ pump }}
        {% elif (hwo > 50 and two > 45) and (two < 55 or two + 5 > hwo) %}
          {{ not pump }}
        {% else %}
          {{ not pump }}
        {% endif %}

This will either return “on” or “off” if I look at this in a Dashboard for example. But as soon as I turn the pump on ( let’s say it wants me to turn it on) then it says to turn it off. If I would let that binary sensor decide to trigger the pump (this is what I want) then the pump would loop on and off.

Is there any chance I can do such things in HA?

For a better understanding I want to explain why I am doing this.

I have two water tanks, one is meant to be for heating the house and one is meant to be for heating the water for the faucets and shower.

There are 2 primary sources of energy, one is a wood heating and one is solar thermal energy. In summer the solar energy will overheat my one tank. This is why I start the pump to get the hot water into the other tank. And what I want to achieve is to automate this process. Otherwise I would need to manually pump the water every 30 minutes or so.

The logic was meant to monitor the faucet tank and if it gets to hot and is hotter than the other tank it should start the pump. As soon as the temperatures even out it should stop pumping to get the faucet tank hot again. And this in a loop until it is 8pm. From 8pm to 9am (at night) the solar energy will not get the water hot anyways. And the next morning, as soon as the faucet tank will get too hot again the logic is supposed to again pump.

I hope you guys can understand and might be able to help me.

Thanks in advance,
Patrick

You’ve got this clause:

 {% if (two < 40 or two > 60) and (two > hwo + 5 or two > 70) %}
          {{ pump }}

… which is basically “if this is true, don’t do anything”.

Then the other two clauses just invert the action.

So what if we use the inverse of the “don’t do anything” clause as a general condition, then just toggle the current state if the condition passes… something like:

template:
  - trigger:
      - platform: state
        to: null
        entity_id:
          - sensor.28_ffd7b0821603_temperatur
          - sensor.28_ffbd5b831603_temperatur
      - platform: state 
        for: "00:05:00"
        entity_id: switch.heizung_pumpe
        to: null
      - platform: time
        at: "09:00:00"
      - platform: time
        at: "20:00:30"
        id: "off"
    variables:
      two: "{{ states('sensor.28_ffd7b0821603_temperatur') | float(0) }}"
      hwo: "{{ states('sensor.28_ffbd5b831603_temperatur') | float(0) }}"
      pump: "{{ is_state('switch.heizung_pumpe', 'on') }}"
      automatik: "{{ is_state('input_boolean.zentral_heizung_automatik', 'on') }}"
      times: "{{  9 <= now().hour < 20 }}"
    condition:
      - or:
           - "{{ not ((two < 40 or two > 60) and (two > hwo + 5 or two > 70)) }}"
           - condition: trigger
             id: "off"
    binary_sensor:
      - name: "Heizungsautomatik Entscheidung"
        unique_id: heizungsautomatik_entscheidung
        state: |
          {% set current = this.state if this is defined else pump %}
          {{ 'off' if (trigger.id == 'off' or not automatik or not times) else not current | bool }}

The trigger based on the pump switch state is probably gratuitous, but it might catch some edge cases…

What is the point of the elif when right after you have the catch-all else which provides the exact same output…?

Yeah well,

if you look back at the python code this will get clearer. The template I created was not final I guess and as I did not understand how exactly this works it might make no sense at all.

But if I would need to write out what the logic has to do it would be:

Lets call it Tank1 and Tank2, as well as Pump, Automatic, Night:

If Automatic is on and it’s not at night:

  • If Tank1 is less than 40 or more than 60 and Tank2 has at least 5 less or Tank 1 is over 70 → Then pump if the pump is not active

The second condition is to deactivate the pump when the temperatures evened out.

(HWO > 50 and TWO > 45) and (TWO < 55 or TWO+5 > HWO)

You can see that I did an inner if Pumpe == ?? in each of those statements. So this will make sure that you can reach the temperature condition, but as soons as the pump state is not the one expected nothing happens.

The inner else in my previous logic never hit if I am not mistaken. Only the one at “night” did.

That should be the case. The Pump state was catching edge cases.

Can you elaborate where I would need to put that piece of code to test it and also how I might be able to enable and disable this logic piece as a Dashboard button? I see you are using or not automatik or not times. So if I understand correctly I could use the entity input_boolean.zentral_heizung_automatik as an entity of a button, which will enable the logic or not.

And times is clear to me. The platform state with for, does that mean that the pump is running for 5 minutes before being turned off? This would eradicate my second elif statement which took care of deactivating the pump.

Thanks

@Didgeridrew : I added your code snippet to my configuration.yaml and made sure that I have all entities in place.

I put a switch to switch on the automatic (input_boolean.zentral_heizung_automatik) and can therefore switch it on and off.

I now have a TWO of 60,5 °C and a HWO of 53,3 °C at 14:45. I would expect the automatic to now turn on the pump to even out the temperatures. But it does not work.

If I look at heizungsautomatik_entscheidung at the dev tools → state then it is “unknown”.

How can I debug this?