RTT Backend Code Examples
This page provides code examples for the data objects that need to be created to support my recurring task tracking (RTT) feature.
Entity Creation
Tasks and Task_actions are created in pairs (more information on how I’ve decided to use them is here). As shown below, a Task is setup in Home Assistant as a trigger based binary sensor with a variety of attributes and a Task_action is setup as an input boolean.
Task Entity (creates binary_sensor.chore_change_keurig_filter):
template:
- trigger:
- platform: time
at: '06:30:00'
- platform: state
entity_id: input_boolean.status_change_keurig_filter
from: 'off'
binary_sensor:
- name: "Chore change keurig filter"
unique_id: chore_change_keurig_filter
state: |
{% set e_string = 'change_keurig_filter' %}
{% from 'tasks.jinja' import determine_task_state %}
{{ determine_task_state(e_string) }}
attributes:
last_updated: "{{ now() | as_local }}"
last_triggered: |
{% set e_string = 'change_keurig_filter' %}
{% from 'tasks.jinja' import get_last_triggered %}
{{ get_last_triggered(e_string) }}
last_completed: |
{% set e_string = 'change_keurig_filter' %}
{% from 'tasks.jinja' import get_last_completed %}
{{ get_last_completed(e_string) }}
trigger_type: "time"
trigger_interval: "Every 2 months on the 1st Sunday"
next_trigger: |
{% set e_string = 'change_keurig_filter' %}
{% from 'tasks.jinja' import get_next_trigger %}
{{ get_next_trigger(e_string) }}
overdue: |
{% set e_string = 'change_keurig_filter' %}
{% from 'tasks.jinja' import check_if_overdue %}
{{ check_if_overdue(e_string) }}
Task_action Entity (creates input_boolean.status_change_keurig_filter):
input_boolean:
status_change_keurig_filter:
name: "Status Change Keurig Filter"
Custom Macros
The Jinja2 code needed to compute many of the attribute values on the task object can be fairly lengthy and would need to be replicated for each individual task which would create too much redundant code. Instead, it is far more manageable to define a custom macro template and to point the attribute segments to the appropriate macro template block. The image below shows this hand-off across files.
Macro Code:
The entirety of the macro file is included below. The first half contains attribute specific code as outlined above. The second half contains many helper code segments to process different calculations (note: some clean up is needed and there are probably a few bugs still in there).
{# These 5 macros below are entity calls that are evaluated which each entity trigger #}
{# {% set all_off = true %}#}
{# Controls state for time-based task/chore binary sensors #}
{# task == "off" and ...removed from "namespace" condition below, keep for now, delete later if now needed#}
{% macro determine_task_state(e_string) %}
{% set e1 = 'binary_sensor.chore_' ~ "" ~ e_string %}
{% set e2 = 'input_boolean.status_' ~ "" ~ e_string %}
{% set next_due_date = strptime(state_attr(e1, 'next_trigger')[:10], '%Y-%m-%d') %}
{% set task = states(e1) %}
{% set task_status = states(e2) %}
{% set vacation_mode = states('input_boolean.vacation_mode') %}
{% if task_status == "off" and vacation_mode == "off" %}
{% set ns = namespace(all_off = true) %}
{% else %}
{% set ns = namespace(all_off = false) %}
{% endif %}
{% if ((now().date() >= next_due_date.date()) or (now().weekday() == 5 and (now().date() + timedelta(days=1)) == next_due_date.date())) and ns.all_off == true %}
on
{% else %}
off
{% endif %}
{% endmacro %}
{# If condition above checks if the current date equals the next_due_date or if the current day is Saturday AND the next_due_date equals tomorrow, this check
allows weekend tasks that are due on Sundays to remain due on Sunday but to activate a day early.
{#{% if now().date() == next_due_date.date() and task == "off" and task_status == "off" and vacation_mode == "off" %} #}
{# Gets the task's last completed date #}
{% macro get_last_completed(e_string) %}
{% set e1 = 'binary_sensor.chore_' ~ "" ~ e_string %}
{% set e2 = 'input_boolean.status_' ~ "" ~ e_string %}
{% set last_completed = state_attr(e1,'last_completed') %}
{{ now() | as_local if is_state(e2, 'on') else last_completed }}
{% endmacro %}
{# Gets the task's last triggered date #}
{% macro get_last_triggered(e_string) %}
{% set e1 = 'binary_sensor.chore_' ~ "" ~ e_string %}
{% set last_triggered = state_attr(e1,'last_triggered') %}
{% from 'tasks.jinja' import determine_task_state %}
{{ now() | as_local if determine_task_state(e_string) | trim == "on" else last_triggered }}
{% endmacro %}
{# Gets the task's next trigger date #}
{% macro get_next_trigger(e_string) %}
{% set e1 = 'binary_sensor.chore_' ~ "" ~ e_string %}
{% set e2 = 'input_boolean.status_' ~ "" ~ e_string %}
{% set next_trigger = state_attr(e1,'next_trigger') %}
{% set chore_status = states(e2) %}
{% if chore_status == "on" %}
{# {% set interval = 3 %}
{% set day_type = "weekend" %}
{% from 'tasks.jinja' import get_next_date %}
{{ get_next_date(e1, interval, day_type) }} #}
{% from 'tasks.jinja' import get_prelim_due_date %}
{{ get_prelim_due_date(e_string) | trim }}
{% else %}
{{ next_trigger }}
{% endif %}
{% endmacro %}
{# Checks whether the task of overdue #}
{% macro check_if_overdue(e_string) %}
{% set e1 = 'binary_sensor.chore_' ~ "" ~ e_string %}
{% set e2 = 'input_boolean.status_' ~ "" ~ e_string %}
{% set next_due_date = strptime(state_attr(e1, 'next_trigger')[:10], '%Y-%m-%d') %}
{% set task = states(e1) %}
{% set task_status = states(e2) %}
{% set today_nt = (now().date() - next_due_date.date()).days %}
{% if today_nt >= 1 and task == "on" and task_status == "off" %}
true
{% else %}
false
{% endif %}
{% endmacro %}
{#**************** END ENTITY CALL MACROS ABOVE ********************#}
{#**************** BEGIN HELPER MACROS BELOW ********************#}
{# Calculates the next trigger date based off the embeded entity trigger_interval #}
{% macro get_prelim_due_date(e_string) %}
{% set e1 = 'binary_sensor.chore_' ~ "" ~ e_string %}
{% set e2 = 'input_boolean.status_' ~ "" ~ e_string %}
{% set search_term = state_attr(e1, 'trigger_interval') %}
{% set current_due_date = strptime(state_attr(e1, 'next_trigger')[:10], '%Y-%m-%d') %}
{# ID's the specified "time_unit" (ex. day, week, weekend, school day, etc.) #}
{% from 'tasks.jinja' import find_term_uoft %}
{% set time_unit = find_term_uoft(search_term) | trim %}
{# {% set time_unit_plural = time_unit ~ "" ~ 's' %} #}
{% if time_unit == "days" or time_unit == "weekdays" or time_unit == "weeks" or time_unit == "months"%}
{% set time_unit_plural = time_unit %}
{% else %}
{# refactor this later as the below is the null condition and will never return a postive in the if-condition below, it's just needed to populate the variable #}
{% set time_unit_plural = time_unit ~ "" ~ 's' %}
{% endif %}
{# enable below to debug #}
{#The time_unit var is: {{ time_unit }}
The time_unit_plural var is: {{ time_unit_plural }} #}
{# Looks to see if "time_unit" (day, week, etc) is plural, if yes, finds the unit count
before the term, ex. "2 months" #}
{% if time_unit_plural in search_term %}
{% set regex_pattern = '(\d+)\s+' ~ time_unit_plural %}
{% set number_before_unit = search_term | regex_findall(regex_pattern) %}
{% if number_before_unit %}
{% set unit_number = number_before_unit[0] %}
{# The number before 'unit' is: {{ unit_number }} #}
{% else %}
{% set unit_number = 1 %}
{% endif %}
{% else %}
{% set unit_number = 1 %}
{% endif %}
{% if time_unit == "day" or time_unit == "school day" or time_unit == "weekday" %}
{% set tu_multiplier = 1 %}
{% set increment_days = (tu_multiplier | int) * (unit_number | int) %}
{% elif time_unit == "weeks" or time_unit == "week" %}
{% set tu_multiplier = 7 %}
{% set increment_days = (tu_multiplier | int) * (unit_number | int) %}
{% elif time_unit == "months" or time_unit == "month" %}
{% set year = current_due_date.year %}
{% set month = current_due_date.month %}
{% set day = current_due_date.day %}
{% set eval_month = (month | int) + (unit_number | int) %}
{% if eval_month > 12 %}
{% set new_month = eval_month - 12 %}
{% set year = year + 1 %}
{% else %}
{% set new_month = eval_month %}
{% endif %}
{% set future_date = strptime('{}-{}-{}'.format(year, new_month | int, day), '%Y-%m-%d') %}
{% set day_diff = (future_date.date() - current_due_date.date()).days %}
{% set increment_days = day_diff %}
{% elif time_unit == "weekend" %}
{% set tu_multiplier = 3 %}
{% set increment_days = (tu_multiplier | int) * (unit_number | int) %}
{% else %}
{# all else path assumes time_unit is not set #}
{% set tu_multiplier = 1 %}
{% set increment_days = (tu_multiplier | int) * (unit_number | int) %}
{% endif %}
{# all that above, just to compute the preliminary next due date #}
{% set prelim_due_date = current_due_date + timedelta(days=increment_days) %}
{# The prelim_due_date is: {{ prelim_due_date }} #}
{% if time_unit == "day" or time_unit == "days" or time_unit == "weekday" or time_unit == "weekdays" or time_unit == "weekend" or time_unit == "school day"%}
{# Checks the proposed date doesn't fall on a holiday, advances it a day if it does #}
{% from 'tasks.jinja' import check_not_holiday %}
{% set days_to_add = check_not_holiday(prelim_due_date) | float(0) %}
{% set prelim_due_date = prelim_due_date + timedelta(days=days_to_add) %}
{# The prelim_due_date after holiday_check is: {{ prelim_due_date }} #}
{# Checks the proposed date doesn't fall on a weekday, advances it to Sunday if it does (want all weekend tasks to be due on Sun) #}
{% if time_unit == "weekend" %}
{% from 'tasks.jinja' import find_weekend_day %}
{% set days_to_add = find_weekend_day(prelim_due_date) | float(0) %}
{% set prelim_due_date = prelim_due_date + timedelta(days=days_to_add) %}
{% endif %}
{# The prelim_due_date after find_weekend_day is: {{ prelim_due_date }} #}
{# Counts the weekend days, if any, between orig & proposed new date #}
{% if time_unit == "weekday" or time_unit == "weekdays"%}
{% from 'tasks.jinja' import count_weekend_days %}
{% set days_to_add = count_weekend_days(current_due_date, prelim_due_date) | float(0) %}
{% set prelim_due_date = prelim_due_date + timedelta(days=days_to_add) %}
{% endif %}
{# The prelim_due_date after count_weekend_days is: {{ prelim_due_date }} #}
{% if time_unit == "school day" %}
{% from 'tasks.jinja' import find_next_school_day %}
{% set prelim_due_date = find_next_school_day(prelim_due_date) | trim | as_datetime %}
{% endif %}
{# The prelim_due_date after find_next_school_day is: {{ prelim_due_date }} #}
{% set final_next_due_date = prelim_due_date %}
{% else %}
{% if ' on ' in search_term %}
{# If yes, then implies the last part included "...on the xx yy" #}
{% from 'tasks.jinja' import find_term_dofw %}
{% set wday = find_term_dofw(search_term) | trim %}
{#{% set wday = wday | int %} #}
{% set weekdays = {
"monday": 0, "tuesday": 1, "wednesday": 2, "thursday": 3,
"friday": 4, "saturday": 5, "sunday": 6
} %}
{% set wday_index = weekdays.get(wday.lower(), -1) %}
{# {{ wday_index }} #}
{% from 'tasks.jinja' import find_in_term_instance %}
{% set w_instance = find_in_term_instance(search_term) | trim %}
{% if w_instance == "" %}
{% set w_instance = "empty" %}
{% endif %}
{% else %}
{% set wday = "empty" %}
{% set wday_index = "empty" %}
{% set w_instance = "empty" %}
{% endif %}
{% if w_instance == "empty" %}
{% from 'tasks.jinja' import find_named_day %}
{% set days_to_add = find_named_day(prelim_due_date, wday_index) | int %}
{% set final_next_due_date = prelim_due_date + timedelta(days=days_to_add) %}
{% else %}
{% from 'tasks.jinja' import find_named_day_instance %}
{% set final_next_due_date = find_named_day_instance(prelim_due_date, w_instance, wday_index) %}
{% set final_next_due_date = final_next_due_date | trim %}
{% set final_next_due_date = final_next_due_date | as_datetime %}
{% endif %}
{% endif %}
{# {{ final_next_due_date }} #}
{{ final_next_due_date.strftime('%Y-%m-%d') }}
{% endmacro %}
{% macro find_next_school_day(check_date) %}
{% set weekend_check = "fail" %}
{% set no_school_check = "fail" %}
{% set no_holiday_check = "fail" %}
{% from 'tasks.jinja' import get_day_adjustments %}
{{ get_day_adjustments(check_date, weekend_check, no_school_check, no_holiday_check) }}
{% endmacro %}
{# Uses recursion to loop until conditions are met#}
{% macro get_day_adjustments(next_date, weekend_check, no_school_check, no_holiday_check) %}
{% if weekend_check == "pass" and no_school_check == "pass" and no_holiday_check == "pass" %}
{# {{ next_date }} #}
{{ next_date.strftime('%Y-%m-%d') }}
{% else %}
{% if next_date.weekday() == 5 %} {# saturday #}
{% set days_to_add = 2 %}
{% elif next_date.weekday() == 6 %} {# sunday #}
{% set days_to_add = 1 %}
{% else %}
{% set days_to_add = 0 %}
{% set weekend_check = "pass"%}
{% endif %}
{% set candidate_date = next_date + timedelta(days=days_to_add) %}
{% from 'tasks.jinja' import check_not_holiday %}
{% set day_adjust = check_not_holiday(candidate_date) | float() %}
{% if day_adjust == 0 %}
{% set no_holiday_check = "pass" %}
{% endif %}
{% set candidate_date = candidate_date + timedelta(days=day_adjust) %}
{% from 'tasks.jinja' import check_school_closed_day %}
{% set day_adjust = check_school_closed_day(candidate_date) | float() %}
{% if day_adjust == 0 %}
{% set no_school_check = "pass" %}
{% endif %}
{% set next_school_day = candidate_date + timedelta(days=day_adjust) %}
{{ get_day_adjustments(next_school_day, weekend_check, no_school_check, no_holiday_check) }}
{% endif %}
{% endmacro %}
{# https://www.timeanddate.com/holidays/us/2025 #}
{# Checks the day does not land on a holiday #}
{% macro check_not_holiday(check_date) %}
{#{% set next_date = strptime(entity_id01, '%Y-%m-%d') %} #}
{% set month = check_date.month %}
{% set day = check_date.day %}
{% set holiday_dates = [
(11, 28),
(12, 25),
(1, 1), (1, 20),
(2, 17),
(5, 26),
(6, 19),
(7, 4),
(9, 1),
(10,13)
] %}
{% if (month, day) in holiday_dates %}
1
{% else %}
0
{% endif %}
{% endmacro %}
{# Called from find_next_school_day macro #}
{# No school days are either vacation day or other days the district has designated as a no school day #}
{# Checks the day does not land on a weekday non school day #}
{% macro check_school_closed_day(check_date) %}
{#{% set next_date = strptime(entity_id01, '%Y-%m-%d') %} #}
{% set month = check_date.month %}
{% set day = check_date.day %}
{% set special_dates = [
(11, 1), (11,11), (11, 25), (11, 26), (11, 27), (11, 29),
(12, 23), (12, 24), (12, 25), (12, 26), (12, 27),
(12, 28), (12, 29), (12, 30), (12, 31),
(1, 1), (1, 2), (1, 3),
(2, 7), (2, 17), (2, 18), (2, 19), (2, 20), (2, 21),
(3, 14),
(4, 7), (4, 8), (4, 9), (4, 10), (4, 11),
(5, 26)
] %}
{% if (month, day) in special_dates %}
1
{% else %}
0
{% endif %}
{% endmacro %}
{# Checks the day does not land on a weekend day
{% macro check_not_weekend(check_date) %}
{% if check_date.weekday() == 5 %} {# saturday
{% set days_to_add = 2 %}
{% elif check_date.weekday() == 6 %} {# sunday
{% set days_to_add = 1 %}
{% else %}
{% set days_to_add = 0 %}
{# {% set weekend_check = "pass" %}
{% endif %}
{{ days_to_add }}
{# {{ next_date + timedelta(days=days_to_add) }}
{% endmacro %} #}
{% macro count_weekend_days(current_date, check_date) %}
{#{% set start = strptime(current_date, '%Y-%m-%d') %}
{% set end = strptime(check_date, '%Y-%m-%d') %} #}
{% set start = current_date %}
{% set end = check_date %}
{% set wc = namespace(weekend_count = 0) %}
{% set total_days = (end - start).days + 1 %} {# Add 1 to include both start and end dates #}
{% for day in range(total_days) %}
{% set current_day = start + timedelta(days=day) %}
{% if current_day.weekday() == 5 or current_day.weekday() == 6 %}
{% set wc.weekend_count = wc.weekend_count + 1 %}
{% endif %}
{% endfor %}
{{ wc.weekend_count | trim }}
{% endmacro %}
{# This s/not be referenced any long, s/b ok to remove!!!!#}
{% macro find_next_saturday(next_date) %}
{% set on_saturday = next_date %}
{% if on_saturday.weekday() != 5 %}
{% if on_saturday.weekday() == 0 %} {# Monday #}
{% set days_to_add = 5 %}
{% elif on_saturday.weekday() == 1 %} {# Tuesday #}
{% set days_to_add = 4 %}
{% elif on_saturday.weekday() == 2 %} {# Wednesday #}
{% set days_to_add = 3 %}
{% elif on_saturday.weekday() == 3 %} {# Thursday #}
{% set days_to_add = 2 %}
{% elif on_saturday.weekday() == 4 %} {# Friday #}
{% set days_to_add = 1 %}
{% elif on_saturday.weekday() == 5 %} {# Saturday #}
{% set days_to_add = 0 %}
{% elif on_saturday.weekday() == 6 %} {# Sunday #}
{% set days_to_add = 6 %}
{% endif %}
{% endif %}
{{ days_to_add }}
{# {{ on_saturday + timedelta(days=days_to_add) }} #}
{% endmacro %}
{% macro find_weekend_day(next_date) %}
{# Set default day to be Sunday #}
{% set target_day = 6 %}
{% from 'tasks.jinja' import find_named_day %}
{% set days_to_add = find_named_day(next_date, target_day) %}
{{ days_to_add }}
{% endmacro %}
{% macro find_named_day(next_date, target_weekday ) %}
{% set on_date = next_date %}
{% set current_weekday = on_date.weekday() %}
{#{% set target_weekday = target_weekday |int() %} #}
{% if current_weekday == target_weekday %}
{% set days_to_add = 0 %}
{% elif current_weekday < target_weekday %}
{% set days_to_add = target_weekday - current_weekday %}
{% else %}
{% set days_to_add = 7 - (current_weekday - target_weekday) %}
{% endif %}
{{ days_to_add }}
{% endmacro %}
{# Will find the 1st (...2nd, 3rd, 4th) instance of a specific weekday (Sun-Sat) for a given month #}
{% macro find_named_day_instance(prelim_due_date, dow_position, dow_index) %}
{% set year = prelim_due_date.year %}
{% set month = prelim_due_date.month %}
{#{% set year = 2024 %}
{% set month = 12 %} #}
{#{% set wday_instance = '2nd' %} # Change this to 'last' to get the last Saturday #}
{% set first_day = strptime('{}-{}-01'.format(year, month), '%Y-%m-%d') %}
{% set test_day = ((first_day + timedelta(days=31)).replace(day=1) - timedelta(days=1)).day %}
{% set last_day = strptime('{}-{}-{}'.format(year, month, test_day), '%Y-%m-%d') %}
{% if dow_position.lower() == 'first' or dow_position == '1st'%}
{% set days_in_month = (last_day - first_day).days + 1 %}
{% for i in range(days_in_month) %}
{% set day = first_day + timedelta(days=i) %}
{% if day.weekday() == dow_index %}
{{ day.strftime('%Y-%m-%d') | trim }}
{% break %}
{% endif %}
{% endfor %}
{% elif dow_position.lower() == 'second' or dow_position == '2nd' %}
{% for i in range((last_day - first_day).days - 22,-1, -1) %}
{% set day = first_day + timedelta(days=i) %}
{% if day.weekday() == dow_index %}
{{ day.strftime('%Y-%m-%d') | trim }}
{% break %}
{% endif %}
{% endfor %}
{% elif dow_position.lower() == 'third' or dow_position == '3rd' %}
{% for i in range((last_day - first_day).days - 15,-1, -1) %}
{% set day = first_day + timedelta(days=i) %}
{% if day.weekday() == dow_index %}
{{ day.strftime('%Y-%m-%d') | trim }}
{% break %}
{% endif %}
{% endfor %}
{% elif dow_position.lower() == 'fourth' or dow_position == '4th' %}
{% for i in range((last_day - first_day).days,-8, -1) %}
{% set day = first_day + timedelta(days=i) %}
{% if day.weekday() == dow_index %}
{{ day.strftime('%Y-%m-%d') | trim }}
{% break %}
{% endif %}
{% endfor %}
{% elif dow_position.lower() == 'last' %}
{% for i in range((last_day - first_day).days,-1, -1) %}
{% set day = first_day + timedelta(days=i) %}
{% if day.weekday() == dow_index %}
{{ day.strftime('%Y-%m-%d') | trim }}
{% break %}
{% endif %}
{% endfor %}
{% endif %}
{% endmacro %}
{# Gets the task's last completed date #}
{% macro find_term_dofw(search_term) %}
{% set days_of_week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] %}
{% set contains_day = false %}
{% set x = 0 %}
{% for day in days_of_week %}
{% set x = x + 1 %}
{% if day|lower in search_term|lower %}
{{ day | trim }}
{% break %}
{% endif %}
{% endfor %}
{% endmacro %}
{% macro find_in_term_instance(search_term) %}
{% set d_instance = ["1st", "2nd", "3rd", "4th", "first", "second", "third", "fourth", "last"] %}
{% for day in d_instance %}
{% if day|lower in search_term|lower %}
{{ day | trim }}
{% break %}
{% endif %}
{% endfor %}
{% endmacro %}
{% macro find_ordinal_suffix(search_term) %}
{% set suffix = ["st ", "nd ", "rd ", "th "] %}
{% set contains_suffix = false %}
{% set x = 0 %}
{% for ord in suffix %}
{% set x = x + 1 %}
{% if ord|lower in search_term|lower %}
{{ ord | trim }}
{% break %}
{% endif %}
{% endfor %}
{% endmacro %}
{% macro find_specific_day(search_term) %}
{% set result = search_term | regex_findall('\d+(?=st|nd|rd|th)') %}
{{ result[0] }}
{% endmacro %}
{% macro find_month_name(search_term) %}
{% set months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October","November","December"] %}
{% for mn in months %}
{% if mn|lower in search_term|lower %}
{{ mn | trim }}
{% break %}
{% endif %}
{% endfor %}
{% endmacro %}
{% macro find_term_uoft(search_term) %}
{% set unit_of_time = ["school day", "weekdays", "weekday", "weekend", "monthly", "months", "month", "weekly", "weeks", "week", "daily", "days", "day" ] %}
{% for word in unit_of_time %}
{% set pattern = " " + word + " " %}
{% if search_term == word or search_term.startswith(word + " ") or search_term.endswith(" " + word) or (pattern in search_term) %}
{{ word | trim }}
{% break %}
{% endif %}
{% endfor %}
{% endmacro %}
{# Calculates the next trigger date based off the entity's last completion date #}
{% macro get_next_date(entity_id01, interval=1, day_type='interval_only') %}
{% set last_date = strptime(state_attr(entity_id01, 'next_trigger')[:10], '%Y-%m-%d') %}
{% set next_date = last_date + timedelta(days=interval) %}
{% set weekend_check = "fail" %}
{% set no_school_check = "fail" %}
{% set no_holiday_check = "fail" %}
{# Checks the proposed date doesn't fall on a holiday, advances it a day if it does #}
{% from 'tasks.jinja' import check_not_holiday %}
{% set days_to_add = check_not_holiday(next_date) | float(0) %}
{% set next_date = next_date + timedelta(days=days_to_add) %}
{# Checks the proposed date doesn't fall on a weekday, advances it to Saturday if it does, want all weekend tasks start on Sat #}
{% if day_type == "weekend" %}
{% from 'tasks.jinja' import find_next_saturday %}
{% set days_to_add = find_next_saturday(next_date) | float(0) %}
{% set next_date = next_date + timedelta(days=days_to_add) %}
{% endif %}
{# Checks the proposed date doesn't fall on a weekend, advances it to monday if it does #}
{% if day_type == "weekday" %}
{% from 'tasks.jinja' import check_not_weekend %}
{% set days_to_add = check_not_weekend(next_date) | float(0) %}
{% set next_date = next_date + timedelta(days=days_to_add) %}
{% endif %}
{% if day_type == "school_day" %}
{% from 'tasks.jinja' import find_next_school_day %}
{% set days_to_add = find_next_school_day(next_date) | float(0) %}
{% set next_date = next_date + timedelta(days=days_to_add) %}
{% endif %}
{{ next_date.strftime('%Y-%m-%d') }}
{% endmacro %}
{% macro find_saturday(entity_id01, interval) %}
{% set interval = (interval) %}
{% set last_date = strptime(state_attr(entity_id01, 'last_completed')[:10], '%Y-%m-%d') %}
{% set next_date = last_date + timedelta(days=interval) %}
{#{% set days_to_add = 0 %} #}
{% set on_saturday = next_date %}
{% if on_saturday.weekday() != 5 %}
{% if on_saturday.weekday() == 0 %} {# Monday #}
{% set days_to_add = 5 %}
{% elif on_saturday.weekday() == 1 %} {# Tuesday #}
{% set days_to_add = 4 %}
{% elif on_saturday.weekday() == 2 %} {# Wednesday #}
{% set days_to_add = 3 %}
{% elif on_saturday.weekday() == 3 %} {# Thursday #}
{% set days_to_add = 2 %}
{% elif on_saturday.weekday() == 4 %} {# Friday #}
{% set days_to_add = 1 %}
{% elif on_saturday.weekday() == 5 %} {# Saturday #}
{% set days_to_add = 0 %}
{% elif on_saturday.weekday() == 6 %} {# Sunday #}
{% set days_to_add = 6 %}
{% endif %}
{% endif %}
{% set on_saturday = next_date + timedelta(days=days_to_add) %}
{{ on_saturday.strftime('%Y-%m-%d') }}
{% endmacro %}