Chore tracking in Home Assistant

Okay everyone, I know there are several chore tracking options around, including Grocy. The unfortunate thing about all these, is none of them fit me exact use case, and so I am trying to develop my own by combining pieces from several, but I am running into some hardships. Hopefully some of those around here that have more knowledge than me can help me with this,

Goals - Overall - Have chore tracking that is native inside home assistant using what already exists. ( this is why grocy is not an option for me, as I will not be the only one using this and I don’t want to have my kids and wife have to log into another layer to track chores) The others I have seen have parts that I want but not all.
Goals - Operation - I want chores that are supposed to be done every so many days, or daily. I want them to be assigned to people in the house randomly but equally (I think the random integration already in Home assistant can be used for this based off what I have read about this , I can have it select a random number in a range I specify, and it will pick each number roughly equally in quantity to any other number in the range. I figure I can just have each number be equal to a child/member of the household)
Proposed way to make work - I think having each chore be seen as some sort of device with multiple sensors may be the way?
For example, Clean The Bathroom is a chore, it has the following sensors:
Person Assigned = Random number between 1 and 7 - Each number is displayed as a name of a member of the household - Can this possibly be linked to users of my home assistant instance?
Date Done: Last time the chore was marked done
Done by: Last person who did the chore, basically the previous state of the person assigned sensor
Date Due: Next time the chore is due
Input date: When person assigned does the chore, they can mark it done and the chore is automatically re-dated with a new due date and assigned to a new person.

I also assume there will need to be a script and makes the due date sensor reset with a new date and makes the person assigned sensor get a new random number.

So, is this possible with what is already in Home assistant?

1 Like

I think it’s possible. Using Input_datetime, input_number to store dates and person numbers, scripts to change them. Custom header allows for per user view. Custom button card for presenting the chore info.

It will be quite involved…

I have no problem with it being involved. I am going to be having time to develop this…as over the next year I will have more free time than usual. I will use this to not only develop this but hopefully learn much more about making integrations for Home Assistant.

My chore tracking for inspiration:
afbeelding1 afbeelding2

When your chore is done, you can tap the button and it will reset the counter. The chore turns orange then red after a certain amount of days. A long tap opens a ‘More info’ popup where you can adjust the date, so you can backdate if you forgot to ‘register’ your task the same day you performed it.

There are other resources, e.g.:


Plenty of inspiration, and what you are describing is certainly doable. You can just use the default HA tool set (template sensors, input_datetime, etc), or build a custom integration if you’re up for some Python coding.

May i ask so bluntly, to share a bit of your config?. It looks really neath.

Sure. Do you mean just the looks (custom button card config)? Or the whole setup including template sensors etc.?

If possible everything :sweat_smile::joy:

Input_datetime and script:

input_datetime:
  bathroom_last_cleaned:
    name: "Bathroom last cleaned"
    has_date: true

scripts:
  cleaned_bathroom:
    alias: 'Bathroom cleaned'
    sequence:
      - service: input_datetime.set_datetime
        data:
          entity_id: input_datetime.bathroom_last_cleaned
          date: "{{ now().timestamp() | timestamp_custom('%Y-%m-%d', true) }}"

Template sensor:

  - platform: template
    sensors:
      bathroom_last_cleaned:
        friendly_name: 'Bathroom last cleaned'
        unit_of_measurement: 'day(s) ago'
# set x= is for ensuring a daily update of this sensor. No longer necessary from HA 0.117.
        value_template: >
          {% set x = states('sensor.date') %}
          {{ ((now().timestamp() - state_attr('input_datetime.bathroom_last_cleaned','timestamp')) / (3600*24)) | round(0, 'floor') |int }}

Button template for custom_button_card:
(depends for styling on theme variables like var(–border-radius) being available in your theme file, if they’re not available just replace them with appropriate values)

button_chores:
  label: ' '
  aspect_ratio: 1/1
  show_state: true
  show_label: false
  show_name: true
  show_icon: true
  size: 20%
  variables:
    alert_green: 7
    alert_orange: 14
  
  # Default styles
  styles:
    card:
      - border-radius: var(--border-radius)
      - filter: opacity(80%)
      - background-color: >
          [[[
            if (entity.state < variables.alert_green)
              return "green";
            if (entity.state < variables.alert_orange)
              return "orange";
            return "red";
          ]]]
    grid:
      - grid-template-areas: '"i" "n" "s"'
      - grid-template-columns: 1fr
      - grid-template-rows: 1fr min-content min-content
    label:
      - font-size: 11px
      - font-family: Helvetica
      - padding: 0px 10px
      - justify-self: start
      - color: var(--label-color-off)
    state:
      - font-size: 11px
      - font-family: Helvetica
      - padding: 0px 10px
      - justify-self: start
      - font-weight: bold
      - color: var(--upcoming-color)
    img_cell:
      - justify-content: start
      - padding-left: 13%
    name:
      - color: var(--upcoming-color)
      - justify-self: start
      - font-weight: bold
      - font-family: Helvetica
      - font-size: 13px
      - margin-top: 0px
      - margin-left: 0px

Lovelace button:

            - type: custom:button-card
              template: button_chores
              entity: sensor.bathroom_last_cleaned
              name: Bathroom cleaned
              icon: mdi:shower
              aspect_ratio: 2/1
              variables:
                alert_green: 7
                alert_orange: 14
              tap_action:
                action: call-service
                service: script.cleaned_bathroom
              hold_action:
                action: more-info
                entity: input_datetime.bathroom_last_cleaned
1 Like

Thanks man, got a little variation on it.

image

1 Like

Nice. I totally forgot about the template sensor. Seems you got it figured out already, but still I added it to my post.

1 Like

No problem, yes i was very curious about your lovelace setup and your button template. The sensor is not that hard.

Thanks again though

Thanks Roelof (@Emphyrio) for sharing your setup! I built on it and made a few changes:

  • get rid of template sensor and script
  • move functionality of buttons into button template

The advantage of the changes is that it now only takes very few lines to add a new chore (add a datetime entity with date only and 8 lines for a new button-card). The disadvantage is that the cards now show the date when the chore was last done and not how long this is ago (e.g. “February 16, 2021” instead of “2 days ago”).

button card template:

button_card_templates:
  button_chores_direct:
    aspect_ratio: 2/1
    show_state: true
    show_label: false
    show_name: true
    show_icon: true
    size: 20%
    variables:
      alert_green: 7
      alert_orange: 14
    tap_action:
      action: call-service
      service: input_datetime.set_datetime
      service_data:
        entity_id: entity
        date: |
          [[[ 
              var d = new Date();
              var year = d.getFullYear();
              var month = d.toLocaleString('en-US', { month : '2-digit' });
              var day = d.toLocaleString('en-US', { day : '2-digit' });
              var result = year + "-" + month + "-" + day;
              return result
          ]]]
    hold_action:
      action: more-info
    styles:
      card:
        - border-radius: var(--border-radius)
        - filter: opacity(100%)
        - background-color: |
            [[[
              if (Date.now()/1000 - entity.attributes.timestamp < variables.alert_green * 24 * 3600)
                return "mediumaquamarine";
              if (Date.now()/1000 - entity.attributes.timestamp < variables.alert_orange * 24 * 3600)
                return "orange";
              return "red";
            ]]]
      grid:
        - grid-template-areas: '"i" "n" "s"'
        - grid-template-columns: 1fr
        - grid-template-rows: 1fr min-content min-content
      label:
        - font-size: 11px
        - font-family: Helvetica
        - padding: 0px 10px
        - justify-self: start
        - color: var(--label-color-off)
      state:
        - font-size: 11px
        - font-family: Helvetica
        - padding: 0px 10px
        - justify-self: start
        - font-weight: bold
        - color: var(--upcoming-color)
      img_cell:
        - justify-content: start
        - padding-left: 13%
      name:
        - color: var(--upcoming-color)
        - justify-self: start
        - font-weight: bold
        - font-family: Helvetica
        - font-size: 13px
        - margin-top: 0px
        - margin-left: 10px

example button card:

          - type: 'custom:button-card'
            template: button_chores_direct
            entity: input_datetime.plants_last_watered
            name: watered plants
            icon: 'mdi:flower'
            variables:
              alert_green: 6
              alert_orange: 13
1 Like

Really like this solution but unsure how to use the template? Where do i set it and how, i know custom_button_card was removed from HACS

Thanks @dicera and @Emphyrio, this is very helpful.

I’ve tweaked a couple things for my own use that I wanted to share back:

  1. Use relative time for the state (e.g. XXXd ago).
  2. Use sensor.date to get today’s date instead of recalculating
  3. Move the button-card variables for the warning times to entity customizations–this allows them to be read as attributes and also used in automations in order to send notifications.
# This requires the following sensors to be configured somewhere:
# sensor:
# - platform: time_date
#   display_options:
#     - 'time'
#     - 'date'
#
# For each input_datetime use the following customizations:
# homeassistant:
#   customize:
#     input_datetime.some_entity:
#       alert_green: 6
#       alert_orange: 13
button_chores_direct:
  aspect_ratio: 2/1
  show_state: true
  show_label: false
  show_name: true
  show_icon: true
  size: 20%
  triggers_update: sensor.time
  tap_action:
    action: call-service
    service: input_datetime.set_datetime
    service_data:
      entity_id: entity
      date: |
        [[[
            return states['sensor.date'].state;
        ]]]
  hold_action:
    action: more-info
  styles:
    card:
      - border-radius: var(--border-radius)
      - filter: opacity(100%)
      - background-color: |
          [[[
            if (Date.now()/1000 - entity.attributes.timestamp < entity.attributes.alert_green * 24 * 3600)
              return "mediumaquamarine";
            if (Date.now()/1000 - entity.attributes.timestamp < entity.attributes.alert_orange * 24 * 3600)
              return "orange";
            return "red";
          ]]]
    grid:
      - grid-template-areas: '"i" "n" "s"'
      - grid-template-columns: 1fr
      - grid-template-rows: 1fr min-content min-content
    label:
      - font-size: 11px
      - font-family: Helvetica
      - padding: 0px 10px
      - justify-self: start
      - color: var(--label-color-off)
    state:
      - font-size: 11px
      - font-family: Helvetica
      - padding: 0px 10px
      - justify-self: start
      - font-weight: bold
      - color: var(--upcoming-color)
    img_cell:
      - justify-content: start
      - padding-left: 13%
    name:
      - color: var(--upcoming-color)
      - justify-self: start
      - font-weight: bold
      - font-family: Helvetica
      - font-size: 13px
      - margin-top: 0px
      - margin-left: 10px
  state_display: |
    [[[
      const time = c => {
        const s = (c / 1000);
        const m = (c / (1000 * 60));
        const h = (c / (1000 * 60 * 60));
        const d = (c / (1000 * 60 * 60 * 24));
        if (s < 60) {
          return parseInt(s) + 's ago';
        } else if (m < 60) {
          return parseInt(m) + 'm ago';
        } else if (h < 24) {
          return parseInt(h) + 'h ago';
        } else {
          return parseInt(d) + 'd ago';
        }
      };
      let last_changed = entity === undefined || time(Date.now() - Date.parse(entity.state));
      return last_changed;
    ]]]

EDIT: Here’s a simplified version of the state_display template for days only:

  state_display: |
    [[[
      const time = c => {
        const d = Math.floor(c / (1000 * 60 * 60 * 24));
        if (d < 1) {
          return 'Today';
        }
        if (d === 1) {
          return 'Yesterday';
        }
        return `${parseInt(d)} days ago`;
      };
      return entity === undefined || time(Date.now() - Date.parse(entity.state));
    ]]]

@bighead85 – Don’t know if you’ve figured this out already, but you need to use the button-card custom Lovelace card. Here is how to use the templates.