RTT Code - Frontend
This page provides lovelace code examples for the frontend of my recurring task tracking feature (RTT). This is the code that is responsible for the output shown in the image below.
There are dependencies on several HACS plugins (listed below), so keep an eye out for their references in the examples.
The UI for this feature is intended to be dynamic. Things like labels, icon colors, badges, and overdue indicators, all will update according to task state, task_action state, and date. To get the same UI treatment across all task items the decluttering card plugin is used to avoid unneccessary code repetition as much as possible. In the three code blocks that follow, the first is the main Lovelace yaml file (…probably good time to mention my UI is in yaml mode) but it is mostly used as a pointer to the decluttering templates that contain all of the formatting elements. These templates make use of the other plugin listed above and contain some templating for the dynamic processing.
This is the first block from the main UI file (note the two pointers to different decluttering templates):
...
cards:
# **** HOUSE TASK STACK ***
- type: custom:vertical-stack-in-card
cards:
- type: custom:decluttering-card
template: task_header
variables:
- header_title: House Tasks
- overdue_chore_count: sensor.house_chore_overdue_count
- std_chore_count: sensor.house_chore_count
- type: custom:decluttering-card
template: task_item
variables:
- chore: binary_sensor.chore_run_vacuum_bot
- status: input_boolean.status_run_vacuum_bot
- chore_label: Pickup for Vacuum Bot
- chore_icon: mdi:robot-vacuum
- chore_what: Run the autobot
Code for the task section header:
task_header:
card:
type: custom:vertical-stack-in-card
cards:
- type: custom:layout-card
layout_type: grid
layout:
grid-template-columns: 55% 45%
grid-template-rows: 1
grid-template-areas: |
"z01 z02"
cards:
- type: custom:mushroom-title-card
title: "[[header_title]]"
view_layout:
grid-area: z01
- type: custom:mushroom-chips-card
chips:
- type: conditional
conditions:
- condition: numeric_state
entity: "[[std_chore_count]]"
below: 1
- condition: numeric_state
entity: "[[overdue_chore_count]]"
below: 1
chip:
type: template
icon: mdi:thumb-up
icon_color: green
card_mod:
style: |
ha-card {
justify-content: center;
--chip-icon-size: 25px;
margin-top: 0.6em
}
- type: conditional
conditions:
- condition: numeric_state
entity: "[[std_chore_count]]"
above: 0
chip:
type: template
entity: "[[std_chore_count]]"
content: |
{{ states('[[std_chore_count]]') }}
card_mod:
style: |
ha-card {
justify-content: center;
font-variant: small-caps;
--chip-background: orange;
--chip-height: 25px;
--chip-font-size: 14px;
--text-color: white;
margin-top: 1.1em
}
- type: conditional
conditions:
- condition: numeric_state
entity: "[[overdue_chore_count]]"
above: 0
chip:
type: template
entity: "[[overdue_chore_count]]"
content: "{{ states('[[overdue_chore_count]]') }}"
card_mod:
style: |
ha-card {
justify-content: center;
font-variant: small-caps;
--chip-background: red;
--chip-height: 25px;
--chip-font-size: 14px;
--text-color: white;
margin-top: 1.1em
}
view_layout:
grid-area: z02
Task item code (Note: to avoid possible confusion, this is a Decluttering Card “template” that uses Jinja templating):
task_item:
card:
type: custom:layout-card
layout_type: grid
layout:
grid-template-columns: 80% 20%
grid-template-rows: 1
grid-template-areas: |
"z01 z02"
cards:
- type: custom:mushroom-template-card
entity: "[[chore]]"
icon: "[[chore_icon]]"
icon_color: |
{% if is_state('[[chore]]', 'on') and is_state('[[status]]', 'off') %}
{% if state_attr('[[chore]]', 'overdue') == 'true' %}
red
{% else %}
orange
{% endif %}
{% elif is_state('[[chore]]', 'off') and is_state('[[status]]', 'off')%}
grey
{% else %}
green
{% endif %}
badge_icon: |
{% set next_trigger = strptime(state_attr('[[chore]]', 'next_trigger')[:10], '%Y-%m-%d') %}
{% set days_b4_trigger = (next_trigger.date() - now().date()).days %}
{% if (days_b4_trigger == 1 and now().weekday() != 5) or (days_b4_trigger == 2 and now().weekday() == 4) %}
mdi:clock-alert-outline
{% endif %}
badge_color: |
{% set next_trigger = strptime(state_attr('[[chore]]', 'next_trigger')[:10], '%Y-%m-%d') %}
{% set days_b4_trigger = (next_trigger.date() - now().date()).days %}
{% if (days_b4_trigger == 1 and now().weekday() != 5) or (days_b4_trigger == 2 and now().weekday() == 4) %}
orange
{% endif %}
primary: "[[chore_label]]"
secondary: |
{% set last_triggered = strptime(state_attr('[[chore]]', 'last_triggered')[:10], '%Y-%m-%d') %}
{% set days_since = (now().date() - last_triggered.date()).days %}
{% set day_number = now().day %}
{% set last_completed = strptime(state_attr('[[chore]]', 'last_completed')[:10], '%Y-%m-%d') %}
{% set days_since_completed = (now().date() - last_completed.date()).days %}
{% set trigger_type = state_attr('[[chore]]','trigger_type') %}
{% set days_b4_trigger = "na" %}
{% if trigger_type == "time" %}
{% set next_trigger = strptime(state_attr('[[chore]]', 'next_trigger')[:10], '%Y-%m-%d') %}
{% set days_b4_trigger = (next_trigger.date() - now().date()).days %}
{% endif %}
{% if is_state('[[chore]]', 'on') and is_state('[[status]]', 'off') %}
{% if state_attr('[[chore]]', 'overdue') == 'true' %}
Overdue - {{days_since | round(0) }} {{'day' if days_since == 1 else 'days' }}
{% else %}
Due
{% endif %}
{% elif days_since_completed == 0 %}
Done: today
{% elif days_since_completed == 1 and (days_b4_trigger == "na" or days_b4_trigger >= 2) %}
Done: yesterday
{% elif (days_b4_trigger == "na" or days_b4_trigger >= 3) %}
Last done: {{ as_timestamp(state_attr('[[chore]]','last_completed')) | timestamp_custom('%Y-%m-%d') }}
{% elif days_b4_trigger == 2 and day_number == 4 %}
On deck for this weekend
{% elif days_b4_trigger == 1 and day_number == 5 %}
Due tomorrow
{% else %}
On deck for tomorrow
{% endif %}
tap_action:
action: fire-dom-event
browser_mod:
service: browser_mod.popup
data:
title: Task detail
content:
type: markdown
content: |
{% set last_complete = strptime(state_attr('[[chore]]', 'last_completed')[:10], '%Y-%m-%d') %}
{% set day_diff = (now().date() - last_complete.date()).days %}
{% set trigger_type = state_attr('[[chore]]', 'trigger_type') %}
{% set trigger_interval = state_attr('[[chore]]', 'trigger_interval') %}
{% if trigger_type == "time" %}
{% set next_trigger = strptime(state_attr('[[chore]]', 'next_trigger')[:10], '%Y-%m-%d') %}
{% set days_b4_trigger = (next_trigger.date() - now().date()).days %}
{% endif %}
{% if day_diff == 0 %}
{% set last_value = 'Today' %}
{% elif day_diff == 1 %}
{% set last_value = 'Yesterday' %}
{% else %}
{% set last_value = as_timestamp(state_attr('[[chore]]','last_completed')) | timestamp_custom('%A, %Y-%m-%d') %}
{% endif %}
{% if trigger_type == "time" %}
{% if days_b4_trigger == 0 %}
{% set next_value = 'Today' %}
{% elif days_b4_trigger == 1 %}
{% set next_value = 'Tomorrow' %}
{% else %}
{% set next_value = as_timestamp(state_attr('[[chore]]','next_trigger')) | timestamp_custom('%A, %Y-%m-%d') %}
{% endif %}
{% else %}
{% if states('[[chore]]') == "on" %}
{% set next_value = 'Today' %}
{% else %}
{% set next_value = state_attr('[[chore]]', 'next_trigger') %}
{% endif %}
{% endif %}
**What**: [[chore_what]]
**When**: {{ trigger_interval }}
**Last**: {{ last_value }}
**Next**: {{ next_value }}
card_mod:
style: |
ha-card {
border: none !important;
box-shadow: none !important;
}
card_mod:
style: |
ha-card {
{% set day_trigger = strptime(state_attr('[[chore]]', 'next_trigger')[:10], '%Y-%m-%d') %}
{% set day_away = (day_trigger.date() - now().date()).days %}
{% if is_state('[[chore]]', 'on') and is_state('[[status]]', 'off') %}
{% if state_attr('[[chore]]', 'overdue') == 'true' %}
--card-secondary-color: red;
{% else %}
--card-secondary-color: orange;
{% endif %}
{% else %}
--card-secondary-color: grey;
{% endif %}
{% if day_away < 2 %}
{% endif %}
}
view_layout:
grid-area: z01
- type: custom:mushroom-chips-card
chips:
#task is on but the task_action status has not been performed
- type: conditional
conditions:
- entity: "[[chore]]"
state: 'on'
- entity: "[[status]]"
state: 'off'
chip:
type: template
entity: "[[status]]"
icon_color: |
{% if state_attr('[[chore]]', 'overdue') == 'true' %}
red
{% else %}
orange
{% endif %}
icon: mdi:checkbox-blank-circle-outline
tap_action:
action: toggle
#task is off b/c the task_action status has recently been marked as done
- type: conditional
conditions:
- entity: "[[chore]]"
state: 'off'
- entity: "[[status]]"
state: 'on'
chip:
type: template
#entity: "[[status]]"
icon_color: green
icon: mdi:check-circle-outline
tap_action:
action: none
#task is off and task_action status is off. Implies no action on either for over a day
- type: conditional
conditions:
- entity: "[[chore]]"
state: 'off'
- entity: "[[status]]"
state: 'off'
chip:
type: template
entity: "[[status]]"
icon_color: grey
icon: mdi:check-circle-outline
tap_action:
action: none
card_mod:
style: |
ha-card {
margin-top: 0.8em !important;
margin-left: 2.0em !important;
}
view_layout:
grid-area: z02
card_mod:
style: |
ha-card {
{% if is_state('[[chore]]', 'on') %}
background: rgba(191, 142, 6, 0.4);
{% endif %}
}