Skip to content

Super Simple Chore Tracker for Home Assistant

Madelena Mak edited this page Jul 2, 2022 · 16 revisions

What is it

Under the Maintenance tab, I kept a list of chores, as shown in the middle column in this screenshot:

HA Chores List

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). 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.

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.

How it works

Basically, all the logic is handled by the front end. The button-cards 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.

How to set up your own

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 &bull; Last done ' + doneStr
      } else { return 'As needed &bull; Last done ' + doneStr }
    ]]]
  custom_fields:
    badge: &field-chore-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.

Clone this wiki locally