-
Notifications
You must be signed in to change notification settings - Fork 59
Super Simple Chore Tracker for Home Assistant
Under the Maintenance tab, I kept a list of chores, as shown in the middle column in this screenshot:
I made it the simplest way I could with what I knew back then since I didn't know how to script (and still doesn't).
This is designed for recurring chores with soft deadlines, as in "X amount of days since it was last done". Like watering plants, I can't do it on a strict daily or weekly schedule - I will end up overwatering it. The best I can do is to monitor the condition and do it as needed, but I should be notified if it has been a while since it was last done.
Here's the thing - a little bit on the natures of chores. Every household has their own 'laws' on when chores should be done, and there isn't a one-size-fits-all chore solution as far as I can find: Recurring chores with hard deadlines (e.g. "Get the exterminator every July 1st"), which is better handled by a calendar; one time tasks (e.g. "Fix overflowing basement toilet") which is better handled by a To Do list app; habits which requires a task done within a hard regular interval (e.g. "Weight training every week") which is better handled by habit apps; or collaborative chores with multiple people involved (e.g. "Refurnish the entire kitchen together") which in my opinion a Trello board is better suited.
I also don't care who did a chore, so all I needed to know is whether I haven't done a chore for too long. There are no assignees function in this tracker.
Each chore has 4 states: Overdue, To Do Soon, Normal, and Done. When a chore is selected, it will reset the last done time to the current moment, and clear any Overdue or To Do Soon status and replace it with Done until the end of the day.
Basically, all the logic is handled by the front end. The button-card
s do the date calculations of whether a task is due or not, by comparing the last changed date of an input button with the time interval set by the corresponding input text.
First, install button-card
to your frontend: https://github.com/custom-cards/button-card
Create the list of chores in a YAML file, such as chores.yaml
:
chore_clean_kitchen_sink: # A unique ID for the chore
name: Clean Kitchen Sink # The name of the chore to be shown on the Chore List
icon: mdi:water-pump # The icon of the chore to be shown on the Chore List
chore_clean_kitchen_counter:
name: Clean Kitchen Counter
icon: mdi:countertop
chore_clean_kitchen_stove:
name: Clean Kitchen Stove
icon: mdi:stove
In configuration.yaml
, include the chores.yaml
file in configuration.yaml
:
input_button chores: !include config/chores.yaml
input_text chores: !include config/chores.yaml
Then create an automation to put all the chores automatically in a group every time Home Assistant starts:
- alias: Home Assistant starts ➔ Update Group - Chores
trigger:
- platform: homeassistant
event: start
- platform: event
event_type: call_service
event_data:
domain: group
service: reload
action:
- service: group.set
data_template:
name: All Chores
object_id: chores
entities: |
{% set ns = namespace(entities=[]) %}
{% for s in states.input_button if s.object_id.startswith('chore_') %}
{% set ns.entities = ns.entities + [ s.entity_id ] %}
{% endfor %}
{{ ns.entities }}
Validate your configuration in HA Developer Tools, and then reload the YAML configuration for "Input Buttons", "Input Texts", and then once those two are reloaded, reload "Groups, Group Entities, and Notify Services".
Double check that the chores are loaded in the States tab of Developer Tools by searching for the entity group.chores
. There should be a list of your chores in the attributes of the entity.
The last steps involve integrating the chores to the front end.
Add a template for a chore item for button-card
. Put the following code under button_card_templates:
in your dashboard YAML. Check the button-card
ReadMe for more info.
chore_card:
variables:
now: [[[ return new Date() ]]]'
last_done: [[[ return new Date(entity.state) ]]]'
diff: >-
[[[ return Math.round((new Date() - new Date(entity.state)) / 1000 /
60 / 60 / 24 ) ]]]
due: >-
[[[ return states[entity.entity_id.replace("input_button",
"input_text")].state ]]]
triggers_update: all
show_label: true
layout: icon_name_state2nd
size: 24px
label: |
[[[
var doneStr
if (variables.diff < 2) {
if (variables.last_done.getDay() == variables.now.getDay()) { doneStr = 'today' } else { doneStr = 'yesterday' }
} else if (isNaN(variables.diff)) {
doneStr = 'unknown'
} else {
doneStr = variables.diff + ' days ago'
}
if (variables.due > 0) {
return 'Every ' + variables.due + ' days • Last done ' + doneStr
} else { return 'As needed • Last done ' + doneStr }
]]]
custom_fields:
badge: |
[[[
if ((variables.diff < 2) && (variables.last_done.getDay() == variables.now.getDay())) { return 'Done' }
if (variables.due > 0) {
if (variables.due < variables.diff) { return 'Overdue' }
if (variables.due < variables.diff + (variables.due / 4)) { return 'To do soon' }
}
]]]
styles:
card:
- margin: 4px 0
- padding: 4px 12px
- background-color: |
[[[
if ((variables.diff < 2) && (variables.last_done.getDay() == variables.now.getDay())) { return 'rgba(var(--rgb-success-color), .33)' }
if ((variables.due > 0) && (variables.due < variables.diff)) { return 'rgba(var(--rgb-error-color), .33)' } else {return 'transparent'}
]]]
grid:
- grid-template-columns: min-content 1fr min-content
- grid-template-areas: '"i n badge" "i s badge" "i l badge"'
img_cell:
- align-self: middle
- text-align: start
- padding: 8px 24px 8px 4px
icon:
- color: |
[[[
if ((variables.diff < 2) && (variables.last_done.getDay() == variables.now.getDay())) { return 'var(--success-color)' }
if (variables.due > 0) {
if (variables.due < variables.diff) { return 'var(--error-color)' }
if (variables.due < variables.diff + (variables.due / 4)) { return 'var(--warning-color)' }
} else { return 'var(--primary-text-color)' }
]]]
name:
- align-self: middle
- justify-self: start
- font-size: var(--body-font-size)
label:
- align-self: middle
- justify-self: start
- font-size: var(--body-font-size)
- opacity: 0.66
custom_fields:
badge:
- background: |
[[[
if ((variables.diff < 2) && (variables.last_done.getDay() == variables.now.getDay())) { return 'var(--success-color)' }
if (variables.due < variables.diff) { return 'var(--error-color)' } else { return 'var(--warning-color)' }
]]]
- padding: 2px
- line-height: 1
- font-size: var(--h6-font-size)
- font-weight: 900
- text-transform: uppercase
- border-radius: 2px
- color: var(--accent-text-color)
tap_action:
action: call-service
service: input_button.press
service_data:
entity_id: entity
hold_action:
action: more-info
entity: >-
[[[ return entity.entity_id.replace("input_button", "input_text")
]]]
Finally, add the list of chores to your dashboard. The auto-entities
frontend module will make things easier if you have a lot of chores, but is optional.
- type: 'custom:auto-entities'
filter:
include:
- group: group.chore_plants
options:
type: 'custom:button-card'
template: chore_card
card:
type: entities
title: Plants
Refresh your dashboard, and your chore list is complete.