Template outputting code as part of the string

I’m trying to return the current tariff based upon the day and time but I’m getting the opening part of the code too. What documents should I read to learn what’s up ?
I’ve used Helpers before but it’s my first crack at a template.
Thanks

  - platform: template 
    sensors: 
      current_tariff: 
        friendly_name: "Current Tariff" 
        value_template: > 
          {% set hour = now().hour %} 
          {% set weekday = now().weekday() %} 
          
          {% if weekday in [0, 1, 2, 3, 4] %}  # Monday to Friday 
            {% if hour >= 14 and hour < 20 %} 
              Peak 
            {% elif (hour >= 7 and hour < 14) or (hour >= 20 and hour < 22) %} 
              Shoulder 
            {% else %} 
              Offpeak 
            {% endif %} 
          {% else %}  # Saturday and Sunday 
            {% if hour >= 7 and hour < 22 %} 
              Shoulder 
            {% else %} 
              Offpeak 
            {% endif %} 
          {% endif %}

Result type: string

  • platform: template
    sensors:
    current_tariff:
    friendly_name: “Current Tariff”
    value_template: >

          # Monday to Friday 
           
            Offpeak
    

did you put that whole block into dev-tools->template?

if so, it’s expected that doing that will output the first part of your yaml. template only evaluates jinja templates and strings. it does not interpret template configurations and such.

1 Like

And for further clarification, a template is defined as anything that falls between the following delimiters (ref):

The default Jinja delimiters are configured as follows:

Any text you place in the template tool that is not between those delimiters will simply be spit back out at you without evaluation.

1 Like

I think he’s put the entire config in the helper state template box.

1 Like

Lol indeed I did. Didn’t know not to. Oops

Fixed. Thanks

          {% set t = "Offpeak" %}
          {% if weekday <5 %}
            {% if hour >= 14 and hour < 20 %} 
              {% set t = "Peak" %}           
            {% elif (hour >= 7 and hour < 14) or (hour >= 20 and hour < 22) %} 
              {% set t = "Shoulder" %} 
            {% endif %}
          {% endif %}
          {{ "Shoulder" if (weekday > 4 and (hour >=7 and hour <22)) else t }}

Just showing how the logic can be reduced a bit :wink:

1 Like

Thanks very much. That’s awesome.

1 Like

I like these games :wink: . This one is a one-liner (line breaks just for readability) with a tariff visualisation:

{{ {'O':'Offpeak','S':'Shoulder','P':'Peak'}
   [("OOOOOOOSSSSSSSPPPPPPSSOO",
     "OOOOOOOSSSSSSSSSSSSSSSOO")
    [now().weekday()>4][now().hour]] }}

Explanation:

('x','y')[now().weekday()>4]

returns 'x' Mon-Fri when the test returns false (0) and 'y' Sat & Sun when the test returns true (1). The ('x','y') is a tuple, like a list but can’t be modified.

Then, 'x' is replaced by the weekday tariff and 'y' by the weekend tariff, and we look up the character associated with the hour. So on a Wednesday:

[("OOOOOOOSSSSSSSPPPPPPSSOO",
  "OOOOOOOSSSSSSSSSSSSSSSOO")
 [now().weekday()>4][now().hour]]

reduces to:

"OOOOOOOSSSSSSSPPPPPPSSOO"[now().hour]

So at 16:00 on a Wednesday, we get a 'P' out of the whole [...] section, and that is the key lookup for the {...} dict, which returns 'Peak':

{{ {'O':'Offpeak','S':'Shoulder','P':'Peak'}['P'] }}

Even shorter version (94 characters) by deriving the tariff arithmetically:

{{('Offpeak','Shoulder','Peak')[min(1+(now().weekday()<5),now().hour//7,(23-now().hour)//2)]}}

This generates the tariff at any combination of hour and weekday by taking the minimum of three lines / slopes:

1+(now().weekday()<5)

is 2 for weekdays 0 to 4, 1 for 5 and 6 using false==0, true==1;

now().hour//7

rises from 0 by 1 every 7 hours (// is the integer division operator); and

(23-now().hour)//2

drops from 11 by 2 every 2 hours. The minimum of all three values (0, 1 or 2) is used to look up the tariff name. Graphical representation:

Definitely got obsessed with this one. Sorry.

4 Likes

Thank you for the breakdown.

Awesome work! We need a template code golf section on this forum.

2 Likes

omg that’s gold lol

1 Like

Seems I’ve got an issue with my tariff template.

After a HA restart, the current tariff state becomes “Unknown” on the History bar, and the log indicates “Current Elec Tariff changed to Shoulder” despite already being in the Shoulder period.

This issue is causing my Daily and Monthly cost entities to reset back to zero. The kWh values in the tariff meters remain intact so the problem appears to point to the tariff template going “Unknown” during the restart.

The cost column on the Energy Dashboard also remains intact.

My setup is :

  • Powercalc gives my total kWh for my Tuya meter
  • Utility Meter helper breaks out a meter for each tariff (3 meters on a Daily reset and 3 for Monthly)
  • Energy Dashboard lets me set the price for each tariff (I’m assuming this step is what creates the ‘cost’ entities that become available. I sum these to give me my Daily and Monthly costs)
  • Current Tariff template sets the current tariff string
  • An automation, shown below, uses that string to select the correct Utility Meter to use for the current tariff.

Is there a way to make the tariff, and hopefully the costs, persistent when restarting ?

alias: Current Power Tariff
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.current_elec_tarif
condition: []
action:
  - service: select.select_option
    data:
      option: "{{ states('sensor.current_elec_tarif') }}"
    target:
      entity_id:
        - select.pc_daily_kwh
        - select.pc_monthly_kwh
        - select.tv_daily_kwh
        - select.tv_monthly_kwh
mode: single

Can you simply change your automation trigger so it doesn’t fire when the sensor changes to unavailable?

alias: Current Power Tariff
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.current_elec_tarif
    not_to:
      - unavailable
      - unknown
condition: []
action:
  - service: select.select_option
    data:
      option: "{{ states('sensor.current_elec_tarif') }}"
    target:
      entity_id:
        - select.pc_daily_kwh
        - select.pc_monthly_kwh
        - select.tv_daily_kwh
        - select.tv_monthly_kwh
mode: single

If you are intent on getting the template sensor to hold its state across a restart, you’d have to change it to a trigger-based template sensor.

1 Like

I just wanted to add my code to this if its not too late. Please note I live in ontario so we have two differnet tariffs based on Summer or winter if you are subscribed to Time of Use billing. This is only for TOU

So I set some variabels liek is it the weekend or holiday or summer its all based on ours. Again just adding theis code here icanse any one else wants it!!

{% set is_holiday = is_state('calendar.canada_on', 'on') %} 
{% set is_weekend = now().isoweekday() in [0, 6] %}
{% set is_summer = now().month >= 5 and now().month < 11 %}

{% if is_holiday or is_weekend %}
  {{ "off-peak" }}
{% elif is_summer %}
  {% if now().hour in [0,1,2,3,4,5,6,19,20,21,22,23] %}
  {{ "off-peak" }}
  {% elif now().hour in [7,8,9,10,17,18] %}
  {{ "mid-peak" }}
  {% elif now().hour in [11,12,13,14,15,16] %}
  {{ "on-peak" }}
  {% endif %}
{% else %}
  {% if now().hour in [0,1,2,3,4,5,6,19,20,21,22,23] %}
  {{ "off-peak" }}
  {% elif now().hour in [7,8,9,10,17,18] %}
  {{ "on-peak" }}
  {% elif now().hour in [11,12,13,14,15,16] %}
  {{ "mid-peak" }}
  {% endif %}
{% endif %}

Also using your example above and some help from Discord server I was able to come up with this code which I like a lot better and is simple to change in the future!

{% set isHoliday = is_state('calendar.canada_on', 'on') %}
{% set isWeekend = now().isoweekday() > 5 %}
{% set isSummer = 4 < now().month < 11 %}
{% if isWeekend or isHoliday %}
  {% set lookup = 'OOOOOOOOOOOOOOOOOOOOOOOO' %}
{% elif isSummer %}
  {% set lookup = 'OOOOOOOMMMMPPPPPPMMOOOOO' %}
{% else %}
  {% set lookup = 'OOOOOOOPPPPMMMMMMPPOOOOO' %}
{% endif %}
{% set map = {'O': 'off-peak', 'M': 'mid-peak', 'P': 'on-peak'} %}
{{ map[lookup[now().hour]] }}