Hi folks,
I’m trying to copy one to-do list to another, but have run into an issue. When copying to the second list, all the separate items from the 1st list end up all together as a single item on the 2nd. I’ll include my code so far, below. Anyone able to help out?
alias: Test 2
description: ""
triggers:
- trigger: state
entity_id:
- input_boolean.test_toggle
from: "off"
to: "on"
conditions: []
actions:
- action: todo.get_items
metadata: {}
data:
status:
- needs_action
target:
entity_id:
- todo.shopping_list
response_variable: items
- action: todo.add_item
metadata: {}
data:
item: >-
{%- for item in items['todo.shopping_list']['items'] %} {{ item.summary
~ "\n" }} {%- endfor %}
target:
entity_id: todo.real_shopping_list
mode: single
the input list looks like this;
However the output list looks like this;
Any help would be appreciated thanks.
You need to used a Repeat for Each action to run each of the items in your template results through the todo.add_item
action.
alias: Test 2
description: ""
triggers:
- trigger: state
entity_id:
- input_boolean.test_toggle
from: "off"
to: "on"
conditions:
- condition: numeric_state
entity_id: todo.shopping_list
above: 0
actions:
- action: todo.get_items
metadata: {}
data:
status:
- needs_action
target:
entity_id:
- todo.shopping_list
response_variable: items
- repeat:
for_each: |
{{ items['todo.shopping_list']['items'] | map(attribute='summary') | list }}
sequence:
- action: todo.add_item
metadata: {}
data:
item: "{{ repeat.item }}"
target:
entity_id: todo.real_shopping_list
mode: single
1 Like
Okay, that’s working, thank you. Do you have any idea how to prevent duplicating on the target list, if the automation is run more than once? For instance, if I add another few items to the source list, and then sync the to the target list again, it adds the entire source list to the target list again. I’m hoping that I can do get it to just sync newly added items.
You would need to add “todo.real_shopping_list” to the entity id list in todo.get_items
, then use the difference()
function so that what goes through the repeat is only the items that are in the base list but not the “real” list.
{% set real = items['todo.real_shopping_list']['items'] | map(attribute='summary') | list %}
{% set base = items['todo.shopping_list']['items'] | map(attribute='summary') | list %}
{{ difference(base, real) }}
That assumes the duplicate is in needs_action
, if you want to check all items and change them from completed to not completed, that gets a bit more complicated… but it’s do-able. It would probably be easiest to do as two separate loops.
Ultimately, I want to be able to do both the needs action and completed parts to sync in both directions. It’s a limitation of the shopping list integration not having a widget, but the to-do lists having one, and the shopping list integration having native support for voice, but the to-do lists needing custom intents. TBH I think it’s a bit of a mess and I can see it being resolved at some point, but currently I need the work around.
Can you walk me through where in the automation the additional code would need to go?
Here’s a rough draft… there are probably a couple things that could be made more efficient, but this should get you started:
alias: Working List items to Real Shopping List
description: ""
triggers:
- trigger: state
entity_id:
- input_boolean.test_toggle
from: "off"
to: "on"
conditions:
- condition: numeric_state
entity_id: todo.shopping_list
above: 0
actions:
- alias: Get NEEDS_ACTION from Working List
action: todo.get_items
metadata: {}
data:
status: needs_action
response_variable: working
target:
entity_id: todo.shopping_list
- action: todo.get_items
metadata: {}
data: {}
response_variable: shopping
target:
entity_id: todo.real_shopping_list
alias: Get ALL items from Real Shopping List
- variables:
working_sums: >
{{ working['todo.shopping_list']['items'] | map(attribute='summary') | list }}
real_all_items: |
{{ shopping['todo.real_shopping_list']['items'] }}
real_needs_sums: |
{{ real_all_items | selectattr('status', 'eq', 'needs_action') | map(attribute='summary') | list }}
real_completed_sums_to_change: |
{{ real_all_items | selectattr('status', 'eq', 'completed') | selectattr('summary', 'in', working_sums)
| map(attribute='summary') | list }}
- repeat:
for_each: "{{real_completed_sums_to_change}}"
sequence:
- action: todo.update_item
data:
status: needs_action
item: "{{repeat.item}}"
target:
entity_id: todo.real_shopping_list
alias: Change Completed to Needs Action
- delay: 1
- repeat:
for_each: >
{# Combine summaries from incomplete items from Real list with the ones that were changed #}
{% set real = real_needs_sums + real_completed_sums_to_change %}
{# Return the summaries from the Working list that still need to be added to the Real list#}
{{ difference(working_sums, real) }}
sequence:
- action: todo.add_item
metadata: {}
data:
item: "{{ repeat.item }}"
target:
entity_id: todo.real_shopping_list
mode: single
You could optionally add further actions in the repeat to remove or “check off” items from the working list. If you want, you can add either of the following after the last action, making sure that it is properly indented to be within the repeat’s sequence:
#Remove
- action: todo.remove_item
data:
item: "{{repeat.item}}"
target:
entity_id: todo.shopping_list
#Mark as completed
- action: todo.update_item
data:
status: completed
item: "{{repeat.item}}"
target:
entity_id: todo.shopping_list
Holy shit, thanks man, that looks good. I’m only getting back online now, since yesterday, so I’ll try it out, but thanks again for the assist.