Scheduler Template Macro

TLDR;
I made a jinja macro which returns the specified value for a scheduled time or if the exact time was not defined, the last valid value. It can easily be used in template helpers.

I’m in need of an easy enough but flexible scheduler to control my heating thermostats. Unfortunately the built-in helper is only binary and has no possibility to specify a value. It’s also just half an hour exactly. After some research I found some very nice but overkill HACS schedulers or complicated automations but nothing lightweight. So I did it myself :slight_smile:

The template
In the template you only need to define your schedule and specify the value for that time. Then you can call the macro which returns the current valid value.

{% from "scheduler.jinja" import get_current_value %}

{% set schedule = {
  "Mon": {
    "07:15":21,
    "17:00":20,
    "22:00":19
  },
  "Tue": {
    "07:15":21,
    "17:00":20,
    "22:00":19
  },
  "Thu": {
    "17:30":20,
    "22:00":19
  },
  "Fri": {
    "07:15":21,
    "17:30":19
  },
  "Sat": {
    "09:00":21,
    "22:00":19
  },
  "Sun": {
    "09:00":21,
    "21:00":19
  }
}%}

{{ get_current_value(schedule) }}

The template is refreshed every minute so theoretically you can define a new value every single minute. More practically there are certain hours when you want to change the value e.g. the temperature of your thermostat. In between the last value is returned. Wednesday is completely missing in the example so from Tuesday evening 22:00 until Thursday evening at 17:15 the value 19 is returned.

The macro:
The macro takes the schedule definition and looks for an entry for today or the closest day before. It does the same for the time. When day and time is selected, it returns the specified value.

{% macro get_current_value(schedule) %}

{% set days = ["","Mon","Tue","Wed","Thu","Fri","Sat","Sun"] %}
{% set ns = namespace(closest_day = now().isoweekday(), closest_time = today_at("00:00"), found = false) %}
{% if schedule[days[ns.closest_day]] is defined %}
  {% for time in schedule[days[ns.closest_day]] %}
    {% if today_at(time) <= now() and today_at(time) >= ns.closest_time %}
      {% set ns.closest_time = today_at(time) %}
      {% set ns.found = true %}
    {% endif %}
  {% endfor %}
{% endif %}
{% for n in range(7) if not ns.found %}
  {% if ns.closest_day > 1 %}
    {% set ns.closest_day = ns.closest_day - 1 %}
  {% else %}
    {% set ns.closest_day = 7 %}
  {% endif %}
  {% if schedule[days[ns.closest_day]] is defined %}
    {% set ns.closest_time = today_at(schedule[days[ns.closest_day]]|list|last) %}
    {% set ns.found = true %}
  {% endif %}
{% endfor %}

{{ schedule[ns.closest_day_name][ns.closest_time.strftime("%H:%M")] }}

{% endmacro %}

That’s it. With this I’m able to schedule my thermostats or anything in the future like next years christmas lights or a ventilation.

Maybe you can use it too.
Feedback much appreciated!

UPDATE
There was a problem with the macro when crossing midnight. The new version is slightly more complicated but should work now.

Very nice! I will try this out for my heating too. Thanks a lot.