Sort Todo Lists

I am using a todo list as an actual list of things to do! Some of these things have due dates/times (I automatically add weekly tasks for example), and some of them do not (tasks that I want to keep track of, but may not get to for weeks or months).

Currently, adding a new item to the list will just add it to the bottom. This can obscure time-sensitive tasks with older but less important tasks, requiring me to scroll to review the whole list.

I would really like to be able to show the listed items in order from oldest to newest due date, with any items without a due date at the end (not sure order matters for those, maybe default sort by oldest creation date?). Other sorting methods would be nice to have too (like the creation date, alphabetical, and reverse order for all of them).

As best I can understand it, there are two ways to achieve this (which are not mutually exclusive).

  1. Augment the Todo card to have a sort order button in the UI, plus a configurable default option. This requires more logic in the card, but could be applied to any Todo entity and would allow for different cards to display any lists (or even the same list several times) in different orders very easily. This would not be reflected in tools outside of HA, however.
  2. Add a sort service call to the Todo entity type, and modify the actual list based on the desired order. This could be nice because it would persist and be reflected outside of HA, but I suspect this would be more challenging to implement consistently across the various integrations that provide Todo lists.

Thanks for your consideration.

While we are sorting, an option to sort alphabetically, for both checked and uncheck items would be appreciated too. My shopping list is now so long that it is hard to find items, and double and triple entries are getting in the list. If the list is sorted, it would be easier to see.

3 Likes

I’ve built an automation that sorts a todo list each time a new item is added.
It keeps name, description and due date (with both formats).
It sorts items with due date first, and then items without due date alphabetically.

This is basic and additional improvements can be made but hopefully it will be useful for some people.

Known bug: if an item is added with the same name that an item that is also in the completed list, the completed item will be removed and duplicate items may be added in the todo list.

It could be simplified by using only one loop, but I kept both for readability.

alias: Todo list sorting
description: >-
  Sort items with due date first, and then items without due date alphabetically
trigger:
  - platform: state
    entity_id:
      - todo.shopping_list
      - todo.test_todo
condition:
  - condition: template
    value_template: "{{ trigger.to_state.state | int > trigger.from_state.state | int }}"
action:
  - variables:
      todolist_entity: "{{ trigger.entity_id }}"
  - service: todo.get_items
    target:
      entity_id: "{{ todolist_entity }}"
    response_variable: items
    data:
      status: needs_action
  - variables:
      items_with_due: >
        {{ items[todolist_entity]['items'] | selectattr('due', 'defined') |
        sort(attribute='due') | list }}
      items_without_due: >
        {{ items[todolist_entity]['items'] | rejectattr('due', 'defined') |
        sort(attribute='summary') | list }}
      sorted_items: |
        {{ items_with_due + items_without_due }}
  - repeat:
      count: "{{ items[todolist_entity]['items'] | length }}"
      sequence:
        - variables:
            item: "{{ sorted_items[repeat.index - 1] }}"
        - service: todo.remove_item
          target:
            entity_id: "{{ todolist_entity }}"
          data:
            item: "{{ items[todolist_entity]['items'][repeat.index - 1].summary }}"
  - repeat:
      count: "{{ sorted_items | length }}"
      sequence:
        - variables:
            item: "{{ sorted_items[repeat.index - 1] }}"
        - service: todo.add_item
          target:
            entity_id: "{{ todolist_entity }}"
          data: |
            {
              "item": "{{ item.summary }}",
              {% if item.description is defined and item.description != '' %}
              "description": "{{ item.description }}",
              {% endif %}
              {% if item.due is defined and item.due != '' %}
                {% if item.due | length == 10 %}
                  "due_date": "{{ item.due }}",
                {% else %}
                  "due_datetime": "{{ item.due }}",
                {% endif %}
              {% endif %}    
            }
mode: single
1 Like

Thanks, that is great! And very funny timing; I have been building and testing a blueprint this week that is very similar, but is for a script that can manually sort the list, vs. doing it when a change occurs. My intent was to post them on GitHub in a few days when I think it is fully working, perhaps I can include your automation there too?

Sure, you can. Indeed, the code could also be a script called from an automation.
Good news, I managed to handle all the properties (name, description and due time).
I’ve updated the code in my post.

I just realized that the completed items are sorted alphabetically by design…
I would have think that the items would be added by their date of completion. I guess the reason for that is that this completion date just does not exist.

Edit: I’ve also added the sorting with items with due dates first in my automation.

Haha interested to see how similar these are, hoping to have time to get out what I have later today or tomorrow.

In my case, I saw I have a bug when I add an item with the same name than another one already completed, it will delete the completed item and create duplicates.

It’s not possible to specify a status in todo.remove_item and it seems it will try to remove items in the completed list first.

I guess I have a crazy workaround for this. I’d need to save the list of completed items, them remove them all with todo.remove_completed_items, then do the sorting of the main list, then add back the completed items…

Edit: Nope, won’t work since todo.add_item does not support status… So even crazier, after all items are removed, add the completed items first, use todo.update_item on them to update their status, then re-add the uncompleted ones…