Todo list: Check if an item exists

I’ve just started using the todo list. It would be great if we could check if an item is already added.

+1. Inability to check means that using todo.remove_item potentially throws an error, and also appears as though todo.remove_item doesn’t handle the continue_on_error: true modifier either. Found I can work around this by executing in parallel, but should either have the ability to check or otherwise gracefully handle the error.

2 Likes

I think to go along with this I would like the ability to check the status of an item. For example, to know if an automation should tell me if should do a specific item on the list (and OP could allow me to verify if it exists or not)

Todo API is a bit strange.

Take update_item, for example. It uses “item” attribute to locate the item.
But you can have two items with the same “item” value. In this case, it will just pick random one.

You also may not have an item with specific “item” attribute. In this case you will get an exception ValueError: Unable to find To-do item ‘NEW ITEM’ logged in system log, not return value “doesn’t exist”, for example.

Apart from having a “find” method, maybe a todo list should have an option for items to be unique? I know I don’t want “BUY MILK” 10 x on my list.
Plus, update_item, the way it is constructed, would make sense.

In this case, add_item will throw an error when adding existing item. Which is fine. Or better yet, return “already exist”, so system log does not get polluted with exceptions. Also, this gives you a chance to call update_item, if needed.

3 Likes

I haven’t updated HA to the latest version yet and I’m wondering if maybe this problem no longer occurs?
It would be nice if each task was an attribute. Then it would be easy to determine whether a given task already exists.

I’m using a small helper script to check if an item with the specified title exists. it returns a boolean and if the item exists, the item itself.

alias: "Todo: Item exists"
sequence:
  - service: todo.get_items
    target:
      entity_id: "{{ list }}"
    response_variable: todo
  - variables:
      result: >
        {% set x = (item in todo[list]['items'] | map(attribute='summary')) %}
        {% set item = todo[list]['items'] | selectattr('summary', 'eq', item) |
        list | first | default([]) %} {{ {'exists': x, 'item': item} }}
  - stop: Finished
    response_variable: result
mode: single
icon: mdi:checkbox-marked-circle-auto-outline
fields:
  item:
    selector:
      text: null
    name: Item
    description: The todo item to check
    required: true
  list:
    selector:
      entity:
        filter:
          - integration: todo
    name: List
    required: true
    description: The todo list entity

Response

exists: true
item:
  summary: Clean the coffee machine
  uid: e1f8ded2-9d7c-11ee-925c-e45f018f63e6
  status: needs_action
  due: "2024-01-11"
  description: Clean the coffee machine internals, the drip trays and the exterior
{% set t = 'Medication' %}
{% set j = {
  "todo.andy_s_tasks": {
    "items": [
      {
        "summary": "take stomach protector",
        "uid": "WWg3a01sQ3Y0OUwzZ01saw",
        "status": "needs_action",
        "due": "2024-01-08"
      },
      {
        "summary": "Check Medication",
        "uid": "cnYxcktOSFc0djVheTV5Zg",
        "status": "needs_action",
        "due": "2024-01-08"
      },
      {
        "summary": "Make Vitamin C Drink",
        "uid": "RE9rUFZfR0JHc3FXS1E4Sw",
        "status": "needs_action",
        "due": "2024-01-08"
      },
      {
        "summary": "Wash Feet",
        "uid": "Wm53V3hJQzRrU196OEhscg",
        "status": "needs_action",
        "due": "2024-01-07"
      },
      {
        "summary": "Empty Bins",
        "uid": "Tm5PS1hpT3Ffd2VCYWdrNg",
        "status": "needs_action",
        "due": "2024-01-04"
      },
      {
        "summary": "Hoover a room",
        "uid": "SzB2R0IwTWZfdkdodlZrVg",
        "status": "needs_action",
        "due": "2024-01-04"
      }
    ]
  }
}
 %}
{% set j = j['todo.andy_s_tasks']['items'] %}
{{ j|selectattr('summary','search','Medication')|list }}

returns:

[{'summary': 'Check Medication', 'uid': 'cnYxcktOSFc0djVheTV5Zg', 'status': 'needs_action', 'due': '2024-01-08'}]

and if it doesn’t exist - it returns [] (a list of count 0)

Just to be clear - it only needs one line:

{% set found =  todo['todo.my_todo_list']['items']|selectattr('summary','search','my search text')|list %}

found|list|count will either be 0 if the item is not found, or a count over 0 if one or more entries matching the search were found.
If you need to update the item - you have all it’s data:

{% set todo_name, todo_due_date = found[0]['summary'], found[0]['due'] %}
2 Likes

can you please add the automation that sending the data to the script please?

We really need something to iteract better throught the items of a todo-list.
I need a script to DELETE REPEATED ENTRIES in a todo-list; maybe something that got the source list, then created a new list, and then add one by one checking if there are duplicates, and then replacing the old list with the new one (just a suggestion / thought). I also need to remove similar items, like for instance, "Coffee " and “coffee” or “Coffeé”, standarizing the list in some way (maybe capitalize the first letter of all items).
Can someone PLEASE help me with this??? Can this be done? It is something I need really bad… lol

PS: I cannot use the command_line solution, because my list is not a .json file, it is created by the google_keep_sync integration. I can’t even locate this list in my files, including .storage…

Thanks, in advance!

This script will check if an item exists in a specified to do list:

alias: "Todo: Item exists"
sequence:
  - service: todo.get_items
    target:
      entity_id: "{{ list }}"
    response_variable: todo
  - variables:
      result: >
        {% set x = (item in todo[list]['items'] | map(attribute='summary')) %}
        {% set item = todo[list]['items'] | selectattr('summary', 'eq', item) |
        list | first | default([]) %} {{ {'exists': x, 'item': item} }}
  - stop: Finished
    response_variable: result
mode: single
icon: mdi:checkbox-marked-circle-auto-outline
fields:
  item:
    selector:
      text: null
    name: Item
    description: The todo item to check
    required: true
  list:
    selector:
      entity:
        filter:
          - integration: todo
    name: List
    required: true
    description: The todo list entity

If you combine this with an action to get items from the first list and then iterate through each item checking if it already exists in a second list, adding it if not and then deleting from the original list, this should achieve what you want

Are you sure, the HA todo-list is for you? :slight_smile: Sorry to say, but what you’re trying to do is something like a full blown Todo-App like Superlist, Todoist or similar.

I wouldn’t want all that blown up stuff in a HA todo-list. :slight_smile: It should be a basic todo-list, if more is needed, there are already solutions for that.

Well, I am sure they will, in the future, cover what I intend to do, it’s the right way. Still missing some features but, knowing this community, they will be adressed sooner or later.
To answer to you, I finnally managed to get my shopping list fully synked with google keep and through that to home assistant, 2 ways. I can now add things by google assistant using mobile or smart speakers to any list in google keep and have them synked with home assistant to analyse and automate. Also the other way around.
Like everything in HA, the purpose of having things is to iteract with them and automate with them, if they are only to exist with no other purpose, better not exist at all. So, yes, HA todo-list are for me, they have the potential to evolve, and I am sure of the path they will follow.
I can work with lists in templates in very complex ways, just want to do some basic thing with todo-list as well.

Not so sure that that’s right way though… :slight_smile:

In my opinion, such things aren’t needed in a HA implementation of a todo-list. There are more than enough alternatives around, that do exactly that. And if that’s not enough, than try to get a custom_component in some way to do that, but this really is nothing for an FR, that would belong into core.

These unessential functions can be accomplished by the user itself, like you did and showed in your last post.

Don’t get me wrong, I can see the need for some people like you, but that doesn’t mean it should be included in HA as a core function(s). :slight_smile: It should be handled like any other component in HA: get the basic/standard/default in HA, if more is needed, get an integration/custom_component to do it. :wink:

I understand that. I don’t mind a custom_component to archieve what I need, if someone with more abilities than me is up to it. Also, lots of custom_components that respect the directives of the main code get imported into the core, so nothing new there. Still see the possible future path of the todo-lists, and they have lots of potencial uses:
Example: I now have a binary_sensor for each of several alerts of situations that I want to be notified of as soon as I get home (Mail recieved, washing machine done, dehumidifier full, and so on…). I have very complex templates that build the text that my speakers should dictate, even with random pieces of text (from sereval combinations) so they are not so repetitive, according to wich alerts are on; a simple todo-list, making sure there are no duplicates, would be the best, if I could only iteract through the items. It’s logical, it’s home automation, it’s the purpose of HA. Basic, nothing fancy (my templates surelly are more complex). Automating the shopping lists is also basic, if they exist they should allow interaction. If they don’t then agree with you, better use something else, but also remove them from HA. But they are in HA and will have a purpose other than displaying static text that you type in the future. We will see. :slight_smile:

For poof of concept, I think I managed to do what I want, needs some polish and I will probably use an automation that triggers on the update of the shopping list with new items:
What this does, for me:

  • Stores the current todo-list in a variable
  • Cleans completed items of my shopping list
  • Deletes non clompleted items
  • Makes items uniform, in lower case and removing trailing spaces and some strings that my “smart” speaker badly interprets when adding through voice (adds " à" ou " na" at the end of the items)
  • Removes duplicates of the uniform list
  • Adds the newly formated and unique items to the todo list.

And to answer again to you, this funcionallity will probably be simpified sometime in the future. For now:

alias: "Lista de Compras: Clean and Remove Duplicates"
sequence:
  - variables:
      lista_todo: todo.google_keep_compras_supermercado
  - service: todo.get_items
    target:
      entity_id: "{{ lista_todo }}"
    data:
      status: needs_action
    response_variable: lista_compras
  - service: todo.remove_completed_items
    target:
      entity_id: "{{ lista_todo }}"
    data: {}
  - variables:
      lista_antiga_items: "{{ lista_compras[lista_todo]['items']|map(attribute='summary')|list }}"
      num_items_antiga: "{{ lista_antiga_items | count}}"
      lista_nova_items: >-
        {{
        lista_compras[lista_todo]['items']|map(attribute='summary')|map('regex_replace',
        ' +$', '')|map('regex_replace', ' à+$', '') |map('regex_replace', '
        na$', '')|map('lower')|unique|list }}
      num_items_nova: "{{ lista_nova_items | count }}"
  - repeat:
      count: "{{ num_items_antiga }}"
      sequence:
        - service: todo.remove_item
          target:
            entity_id: "{{ lista_todo }}"
          data_template:
            item: "{{ lista_antiga_items[repeat.index-1] }}"
    enabled: true
  - repeat:
      count: "{{ num_items_nova }}"
      sequence:
        - service: todo.add_item
          target:
            entity_id: "{{ lista_todo }}"
          data_template:
            item: "{{ lista_nova_items[repeat.index-1] }}"
    enabled: true
mode: single

Hope it helps to solve someone else’s problems! :slight_smile: :star_struck:

2 Likes

Please check my post above, it doesn’t stop the adding of repeated items, but removes duplicates.

I’ve been wanting something like this for a long time! Thank you very much. I’ll have to modify it for my own use case though, which is slightly different. We don’t clear the checked items from our shopping list because my wife likes to look through the previously checked items and uncheck a few before going to the store. On the other hand, I usually just type the item again when I want to add something to the list. Therefore, we end up with duplicates that I have to go and clean up manually from time to time, but those duplicates end up as checked/completed items, so that’s where I’m the most interested in removing duplicates and standardizing the strings. :slight_smile: Thanks again for this. I hope to find some time to implement it soon. :+1: :trophy:

1 Like