Do you struggle keeping track of the household chores assigned to your kids? Or motivating them to complete their tasks? At one point, we used a whiteboard to keep track of tasks. But it's analogue, got limited space, got no time tracking, can't notify me, it's easy to forget to reset and hard to calculate the weekly allowance based on completed chores. The kids see no progress; except maybe some stickers...

So I thought, why not create a special dashboards for the kids, where they kan keep track of their tasks, their weekly allowance and throw in an element of gamification?

This article outlines a proposal on how you can implement this for your kids. It builds on the basic idea of how I manage repeating household chores, and adds a personalised gamification layer on top of it.

Create household chores for each kid

The first step is to create the tasks for each kid. I will use input booleans and a strict naming convention to make it possible to assign the right task to the right kid.

input boolean:
  vacuum_<kid1>_household_chore:
    name: Vacuum the house
  bedlinens_<kid1>_household_chore:
    name: Change your bed linens
  settable_<kid1>_household_chore:
    name: Set the dinner table

  dishes_<kid2>_household_chore:
    name: Do the dishes
  bedlinens_<kid2>_household_chore:
    name: Change your bed linens
  cleartable_<kid2>_household_chore:
    name: Clear the dinner table      

This way, by looking at the entity_id, we know what input boolean belongs to what kid and what task it represents.

Assigning chores to the kids

Some chores can be expected to be completed every day; like setting or clearing the table. While other chores make sense to do on school days, or every twice week or whatever. This you can control with automations, where you set the tasks to "due" by turning the respective input booleans "off".

automation:
- alias: Chores - every day
  trigger:
  - platform: time
    at: 06:00:00
  action:
  - service: input_boolean.turn_off
    data: {}
    target:
      entity_id:
      - input_boolean.settable_<kid1>_household_chore
      - input_boolean.cleartable_<kid2>_household_chore
  mode: single

- alias: Chores - weekly
  trigger:
  - platform: time
    at: 06:00:00
  condition:
  - condition: time
    weekday:
    - fri
  action:
  - service: input_boolean.turn_off
    data: {}
    target:
      entity_id:
      - input_boolean.vacuum_<kid1>_household_chore
      - input_boolean.dishes_<kid2>_household_chore
  mode: single

- alias: Chores - every other week
  trigger:
  - platform: time
    at: 06:00:00
  condition:
  - condition: time
    weekday:
    - fri
  - condition: template
    value_template: '{{ now().isocalendar()[1] % 2 == 1 }}'
  action:
  - service: input_boolean.turn_off
    data: {}
    target:
      entity_id:
      - input_boolean.bedlinens_<kid1>_household_chore
      - input_boolean.bedlinens_<kid2>_household_chore
  mode: single

The first automation will set chores to "due" evey day at 6am. The second automation will set chores to "due" every friday at 6am. The third automation will set chores to "due" every friday at 6am, as long as the current week-number is an odd number (so every second week).

Displaying chores that are due

To get a list over which chores are due for a specific kid, use Thomas Lovens brilliant auto-entities card:

- type: custom:auto-entities
  filter:
    include:
      - entity_id: input_boolean.*_<kid1>_household_chore
        state: "off"
  show_empty: false
  card:
    type: entities
    title: Household chores due for <kid1>
    show_header_toggle: false
    show_state: false

Notifying

Of course, kids nowadays are used to getting notifications for everything (I sound really old saying it like this), so you'd better create a binary sensor which will turn "on" whenever there is a new task to be done.

- unique_id: task_due_<kid1>
  attributes:
    friendly_name: Task due <kid1>
  state: "{{ (states |selectattr('entity_id', 'search', 'input_boolean.*_<kid1>_household_chore') |selectattr('state', 'in', 'off')|list)|length > 0 }}"

When the binary sensor turns on, or maybe every afternoon at a set time, if the binary sensor is on, you just send a notification to your kid's mobile phone.

automation:
- alias: Tasks due notification
  trigger:
  - platform: time
    at: 16:00:00
  condition:
  - condition: state
    entity_id: binary_sensor.template_task_due_<kid1>
    state: on
  action:
  - service: notify.mobile_app_<kid1>_mobile
    continue_on_error: true
    data:
      message: 'Hey - complete those due tasks!'
  mode: single

Accumulating points

Now we have a way of assigning tasks, notifying your kids and a way to show them their todo-list. Now we need a mechanism to grant points whenever the task is done: simply use e.g. "(+1)" in the name field of the input booleans you defined earlier, to define how many points each task is worth, like this:

input boolean:
  vacuum_<kid1>_household_chore:
    name: Vacuum the house (+3)
  bedlinens_<kid1>_household_chore:
    name: Change your bed linens (+5)
  settable_<kid1>_household_chore:
    name: Set the dinner table (+1)

  dishes_<kid2>_household_chore:
    name: Do the dishes (+3)
  bedlinens_<kid2>_household_chore:
    name: Change your bed linens (+5)
  cleartable_<kid2>_household_chore:
    name: Clear the dinner table (+1)

Now, when seeing the todo list, the kid will know how much each task is worth before completing it. It would thus be natural to grant more points to certain weekly tasks, and less points to daily tasks. That way, it's okay to skip a daily task now and then, but not to often, or the kid won't accumulate enouth points to get a decent weekly allowance (we'll soon get to calculating the allowance).

To actually accumulate points based on the (+1) and other values, do the following:

  1. Set up an input_number to keep track of the score
  2. Create an automation that catches state changes on the input_booleans that are associated to a weekly allowance
  3. Send the state change of the input_booleans to a script to increment the score stored in the input_number

In code, it looks like this:

input_number:
  household_chores_<kid1>:
    name: <kid1> - points
    max: 100
    min: 0
    step: 1

automation:
- alias: Grant points for chores
  trigger:
  - platform: state
    entity_id:
    - input_boolean.vacuum_<kid1>_household_chore
    - input_boolean.bedlinens_<kid1>_household_chore
    - input_boolean.settable_<kid1>_household_chore
    - input_boolean.dishes_<kid2>_household_chore
    - input_boolean.bedlinens_<kid2>_household_chore
    - input_boolean.cleartable_<kid2>_household_chore
  condition: []
  action:
  - service: script.household_points
    data:
      triggered_id: '{{ trigger.entity_id }}'
      from_state: '{{ trigger.from_state.state }}'
      to_state: '{{ trigger.to_state.state }}'
  mode: single

script:
    household_points:
      variables:
        grant_to_entity_id: "{{ 'input_number.household_chores_' + triggered_id.split('input_boolean.')[1].split('household_chore')[0].split('_')[1] }}"
        grant_points: "{{ state_attr(triggered_id, 'friendly_name').split('(+')[1].split(')')[0]|int }}"
      sequence:
        - choose:
            - conditions: "{{ from_state == 'off' and to_state == 'on' }}"
              sequence:
                - service: input_number.set_value
                  target:
                    entity_id: "{{ grant_to_entity_id }}"
                  data:
                    value: "{{ states(grant_to_entity_id)|int + grant_points }}"

Now we are able to count upwards. But we still need a way to reset the score each week, and maybe the adults need a way to deduct points for a task not so well done.

Resetting the score each week is a simple automation every sunday night / monday morning:

automation:
- alias: Reset household chore points
  description: ''
  trigger:
  - platform: time
    at: 06:00:00
  condition:
  - condition: time
    weekday:
    - mon
  action:
  - service: input_number.set_value
    data:
      value: 0
    target:
      entity_id:
      - input_number.household_chores_<kid1>
      - input_number.household_chores_<kid2>
  mode: single

Deducting points for a task not so well done, is a matter of having a parent dashboard with the kids tasks, and a way of resetting a task while at the same time deducting the points the task was worth.

Deduction of points can be done by setting a "deduct points" input_boolean to "true", before flipping a completed task from "on" to "off. Thus, we need to define an input_boolean for the deduct part first:

input_boolean:
  deduct_score:
    name: Chore not approved

We'll use the input_boolean in a dashboard, where the parent can flip the "Chore not approved" to true when they want to deduct a score. It can look like this:

# the dashboard part to turn on the "deduct score flag"
- type: entities
  entities:
  - entity: input_boolean.deduct_score

# the dashboard part where you see and change state of all completed and not completed tasks for <kid1>
- type: custom:auto-entities
  filter:
    include:
      - entity_id: input_boolean.*_<kid1>_household_chore
  show_empty: false
  card:
    type: entities
    show_header_toggle: false
    show_state: false

If "deduct_score" is "on", when a household_chore os flipped to "off", it means the task was rejected and points deducted.

To actually do this, we also need to adjust the script that is called whenever a chore changes state, so it catches up on the deduction, like this:

script:
  household_points:
    variables:
      grant_to_entity_id: "{{ 'input_number.household_chores_' + triggered_id.split('input_boolean.')[1].split('household_chore')[0].split('_')[1] }}"
      grant_points: "{{ state_attr(triggered_id, 'friendly_name').split('(+')[1].split(')')[0]|int }}"
      deduct: "{{ states('input_boolean.deduct_score') }}"
    sequence:
      - choose:
  - choose:
      - conditions: "{{ from_state == 'on' and to_state == 'off' and deduct == 'on' }}"
        sequence:
          - service: input_number.set_value
            target:
              entity_id: "{{ grant_to_entity_id }}"
            data:
              value: "{{ states(grant_to_entity_id)|int - grant_points }}"
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.deduct_score        
      - conditions: "{{ from_state == 'off' and to_state == 'on' }}"
        sequence:
          - service: input_number.set_value
            target:
              entity_id: "{{ grant_to_entity_id }}"
            data:
              value: "{{ states(grant_to_entity_id)|int + grant_points }}"

The script will also reset the deduction input_boolean; so you have to flip it to true every time you want to reject a chore.

Making it look nice

So, it's not much gamified unless the kid kan see the progress and the weekly allowance payout live. To get an idea of the progress of the current week, I built a star based progress indicator, with different levels corresponding to achieving a certain number of points. For this, I use a set of template entities and a custom button card to display the current state of progress.

For each kid, you first need to create a set of template sensors like this:

- name: Household chores <kid1> points
  unique_id: household_chores_<kid1>_points
  state: >
    {{ states('input_number.household_chores_<kid1>')|int(default=0) }}

- name: Household chores <kid1> stars
  unique_id: household_chores_<kid1>_stars
  icon: mdi:star
  state: >
    {% set points = states('input_number.household_chores_<kid1>')|int(default=0) %}
    {% if points > 14 %}
    {%   set stars = 5 %}
    {% elif points > 9 %}
    {%   set stars = 4 %}
    {% elif points > 6 %}
    {%   set stars = 3 %}  
    {% elif points > 3 %}
    {%   set stars = 2 %}
    {% elif points > 0 %}
    {%   set stars = 1 %}
    {% else %}
    {%   set stars = 0 %}
    {% endif %}
    {{ stars }}

- name: Household chores <kid1> payout
  unique_id: household_chores_<kid1>_payout
  icon: mdi:cash
  unit_of_measurement: "NOK"
  state: >
    {% set stars = states('sensor.household_chores_<kid1>_stars')|int %}
    {% if stars == 5 %}
    {%   set payout = 50 %}
    {% elif stars == 4 %}
    {%   set payout = 40 %}
    {% elif stars == 3 %}
    {%   set payout = 30 %}  
    {% elif stars == 2 %}
    {%   set payout = 20 %}
    {% elif stars == 1 %}
    {%   set payout = 10 %}
    {% else %}
    {%   set payout = 0 %}
    {% endif %}
    {{ payout }}

- name: Household chores <kid1> robux
  unique_id: household_chores_<kid1>_robux
  icon: mdi:cash
  unit_of_measurement: "Robux"
  state: >
    {{ ((states('sensor.household_chores_<kid1>_payout')|int * 8) / 400)|int * 400 }}

- name: Household chores <kid1> payout rest after robux
  unique_id: household_chores_<kid1>_payout_rest_after_robux
  icon: mdi:cash
  unit_of_measurement: "NOK"
  state: >
    {{ (((states('sensor.household_chores_<kid1>_payout')|int * 8) % 400) / 8)|int }}
  • The first sensor is created to more easily show an integer without the decimal marker in a button card.
  • The second sensor is used to measure the kid's progress on this weeks tasks.
  • The third sensor defines the payout for achieving the different levels from the previous sensor. You could instead get the payout from input numbers to make it more dynamic.
  • The fourth sensor converts roughly from NOK (Norwegian kroner) to Robux, making sure you can only buy multiples of 400 Robux with your payout.
  • The fifth sensor calculates the payout minus the multiples of 400 Robux you are able to buy with the payout.

(The thing with the Robux, is that one of my kids is more motivated by Robux than real money.)

Now, to use these sensors in a dashboard for the kid to display progress, you need two custom button templates that require the brilliant custom button cards by RomRider.

To create the templates, you may create a "rating-templates.yaml" in your config-directory with the following content:

button_rating:
  show_name: false
  show_state: false
  show_icon: false
  styles:
    card:
      - box-shadow: none
      - border: 0
      - background: transparent
      - width: 275px
    grid:
      - grid-template-areas: '". title title title title title ." ". first second third fourth fifth ." ". first_points second_points third_points fourth_points fifth_points ." ". points_week points_week points_week points_week points_week ." ". money_week money_week money_week money_week money_week ." ". robux_week robux_week robux_week robux_week robux_week ."  ". rest_week rest_week rest_week rest_week rest_week ."'
      - grid-template-columns: auto
      - grid-gap: 0px
    custom_fields:
      title:
       - font-size: 1.5em
       - padding-bottom: 15px
      first_points:
        - font-size: 10px
      second_points:
        - font-size: 10px
      third_points:
        - font-size: 10px
      fourth_points:
        - font-size: 10px
      fifth_points:
        - font-size: 10px
      points_week:
        - font-size: 1em
        - text-align: center
        - padding-top: 15px
        - padding-bottom: 10px
      money_week:
        - font-size: 0.7em
        - text-align: center
        - padding-bottom: 5px
      robux_week:
        - font-size: 0.7em
        - text-align: center
        - padding-bottom: 5px
      rest_week:
        - font-size: 0.7em
        - text-align: center

button_rating_star:
  color_type: icon
  icon: mdi:star
  color: rgb(68, 115, 158)
  show_name: false
  show_state: false
  styles:
    card:
      - background-color: transparent
      - box-shadow: none
      - border: 0
    icon:
      - width: 33px
      - height: 33px

When you have the template in place, you can create a custom dashboard for the kid like this:

lovelace:
  dashboards:
    smarthome-yaml:
      mode: yaml
      title: Chores <kid1>
      icon: mdi:home-assistant
      show_in_sidebar: true
      filename: chores-<kid1>.yaml

And then you create a file in the config directory with the name "chores-.yaml" with the following content:

button_card_templates: !include rating-templates.yaml
views:
  - title: Gamified chores
    type: panel
    badges: []
    cards:
      - type: custom:mod-card
        style:
          .: |
            ha-card {
              background-color: #1c1c1c;
            }
          hui-grid-card$: |
            #root {
              justify-items: center !important;
              box-shadow: none;
              border: 0;
            }
        card:
          type: grid
          columns: 1
          square: false
          cards:    
            - type: custom:button-card
              styles:
                card:
                  - margin-top: 40px
                  - margin-left: 5px
                  - background-color: "#202020"
                  - border: solid 1px purple
              template: button_rating
              entity: sensor.household_chores_<kid1>_stars
              custom_fields:
                title: Weekly allowance
                first:
                  card:
                    type: custom:button-card
                    template: button_rating_star
                    entity: sensor.household_chores_<kid1>_stars
                    state:
                      - value: 1
                        operator: ">="
                        styles:
                          icon:
                            - color: var(--paper-item-icon-active-color)
                first_points: 1 point
                second:
                  card:
                    type: custom:button-card
                    template: button_rating_star
                    entity: sensor.household_chores_<kid1>_stars
                    state:
                      - value: 2
                        operator: ">="
                        styles:
                          icon:
                            - color: var(--paper-item-icon-active-color)
                second_points: 4 points
                third:
                  card:
                    type: custom:button-card
                    template: button_rating_star
                    entity: sensor.household_chores_<kid1>_stars
                    state:
                      - value: 3
                        operator: ">="
                        styles:
                          icon:
                            - color: var(--paper-item-icon-active-color)
                third_points: 7 points
                fourth:
                  card:
                    type: custom:button-card
                    template: button_rating_star
                    entity: sensor.household_chores_<kid1>_stars
                    state:
                      - value: 4
                        operator: ">="
                        styles:
                          icon:
                            - color: var(--paper-item-icon-active-color)
                fourth_points: 10 points
                fifth:
                  card:
                    type: custom:button-card
                    template: button_rating_star
                    entity: sensor.household_chores_<kid1>_stars
                    state:
                      - value: 5
                        operator: ">="
                        styles:
                          icon:
                            - color: var(--paper-item-icon-active-color)
                fifth_points: 15 points
                points_week: >
                  [[[ return `<ha-icon icon="mdi:progress-star" style="font-size: 20px; width: 25px; height: 25px; "></ha-icon> ${states['sensor.household_chores_<kid1>_points'].state} points this week!` ]]]
                money_week: |
                  [[[
                    return `Which equals ${states['sensor.household_chores_<kid1>_payout'].state} kr`
                  ]]]                
                robux_week: |
                  [[[
                    if (states['sensor.household_chores_<kid1>_robux'].state > 0) {
                      return `or ${states['sensor.household_chores_<kid1>_robux'].state} Robux`
                    }
                  ]]]
                rest_week: |
                  [[[
                    if (states['sensor.household_chores_<kid1>_payout_rest_after_robux'].state > 0 && states['sensor.household_chores_<kid1>_robux'].state > 0) {
                      return `and ${states['sensor.household_chores_<kid1>_payout_rest_after_robux'].state} kr`
                    }
                  ]]]                
            - type: custom:auto-entities
              filter:
                include:
                  - entity_id: input_boolean.*_<kid1>_household_chore
                    state: "off"
              show_empty: false
              card:
                type: entities
                card_mod:
                  style: |
                    ha-card {
                      background: none;
                      box-shadow: none;
                    }            
                title: Tasks due
                show_header_toggle: false
                show_state: false

The above code requires the mod-card plugin by Thomas Loven.

I would suggest removing borders from the current default theme, if you use it. You could do that by definining the following code in your configuration file to create a very simple custom theme.

frontend:
  themes:
    My Custom Theme:
      ha-card-border-color: rgba(0,0,0,0) ## transparent

Then you need to change the theme for each device you'll use the dashboard on. The custom theme won't appear in the list of themes before you restart Home Assistant. Make sure to check the config and backup beforehand.

The end

This is how I made a gamified score based household chore system for Home Assistant. I hope you got inspired, and maybe implement some of these ideas in your own smart home.

Previous Post Next Post