This automation synchronizes two to-do lists bidirectionally. I use it to ensure that any changes made to my Mealie shopping list are reflected in my Bring! shopping list and vice versa. Since the Bring! app is more practical for shopping, this setup helps keep everything up to date automatically.
At first, I thought syncing two to-do lists wouldn’t be too complicated—but wow, was I wrong. This automation took me hundreds of attempts, and I accidentally deleted and created list entries countless times. I’m not great with templates/Jinja, which might have contributed to the struggle, but since I couldn’t find much information on this topic, I decided to share my solution to hopefully save others some headaches.
Warning: Proceed with Caution When Automating To-Do Lists
Synchronizing to-do lists through automation can potentially disrupt your lists if not implemented carefully. While this solution works reliable in my specific setup, I’ve encountered numerous edge cases that make this process surprisingly complex.
Before Implementation:
- Create two test lists to experiment with
- Thoroughly test the automation in various scenarios
- Verify behavior when lists are edited simultaneously, offline, or during sync processes
How it works:
- The automation triggers two scripts that update the other shopping list whenever one of the lists changes.
- As an extra safeguard, it also triggers a sync every 15 minutes, updating the lists based on which one was modified more recently.
- Items are never deleted, only marked as done, to prevent accidental loss of entries.
- A 5-second delay is included to slow things down a bit and allow the user to finish editing an entry before the sync kicks in.
- This isn’t a perfect solution, but after countless attempts, I’m just happy it finally works! If you have suggestions for improvements, I’d love to hear them.
The automation which triggers the scripts for syncing:
alias: Mealie | Mealie & Bring | Sync
description: Synchronizes the shopping lists between Mealie and Bring! bidirectionally.
triggers:
- entity_id: todo.mealie_wocheneinkauf
trigger: state
alias: Mealie list was updated
id: Mealie list was updated
- entity_id: todo.einkaufen
trigger: state
alias: Bring! list was updated
id: Bring! list was updated
- alias: 15-minute trigger
trigger: time_pattern
id: 15-minute trigger
minutes: /15
actions:
- delay:
hours: 0
minutes: 0
seconds: 5
milliseconds: 0
- choose:
- conditions:
- condition: trigger
id:
- Mealie list was updated
sequence:
- action: script.mealie_todo_liste_synchronisieren_mealie_liste
metadata: {}
data: {}
alias: Update Mealie list
alias: Mealie list was updated (update Bring!)
- conditions:
- condition: trigger
id:
- Bring! list was updated
sequence:
- action: script.mealie_todo_liste_synchronisieren_bring_liste
metadata: {}
data: {}
alias: Update Bring! list
alias: Bring! list was updated (update Mealie)
- conditions:
- condition: trigger
id:
- 15-minute trigger
sequence:
- alias: Update lists based on most recent changes
if:
- condition: template
value_template: >-
{{ states.todo.einkaufen.last_changed >
states.todo.mealie_wocheneinkauf.last_changed }}
then:
- sequence:
- action: script.mealie_todo_liste_synchronisieren_bring_liste
metadata: {}
data: {}
alias: Update Bring! list
- action: script.mealie_todo_liste_synchronisieren_mealie_liste
metadata: {}
data: {}
alias: Update Mealie list
else:
- sequence:
- action: script.mealie_todo_liste_synchronisieren_mealie_liste
metadata: {}
data: {}
alias: Update Mealie list
- action: script.mealie_todo_liste_synchronisieren_bring_liste
metadata: {}
data: {}
alias: Update Bring! list
mode: single
Script: Update Bring! List
sequence:
- alias: Update list | Bring!
sequence:
- alias: Retrieve existing entries from Bring! list
target:
entity_id: todo.einkaufen
data:
status:
- needs_action
- completed
response_variable: source_items
action: todo.get_items
- alias: Retrieve existing entries from Mealie list
target:
entity_id: todo.mealie_wocheneinkauf
data:
status:
- needs_action
- completed
response_variable: existing_items
action: todo.get_items
- alias: Add new entries to Mealie list
repeat:
for_each: |-
{{
source_items['todo.einkaufen']['items']
| selectattr('status', 'eq', 'needs_action')
| rejectattr('summary', 'in', existing_items['todo.mealie_wocheneinkauf']['items'] | map(attribute='summary') | list)
| list
}}
sequence:
- target:
entity_id: todo.mealie_wocheneinkauf
data:
item: "{{ repeat.item.summary }}"
action: todo.add_item
- alias: Mark completed items as done in Mealie
repeat:
for_each: |-
{{
source_items['todo.einkaufen']['items']
| selectattr('status', 'eq', 'completed')
| selectattr('summary', 'in', existing_items['todo.mealie_wocheneinkauf']['items'] | selectattr('status', 'ne', 'completed') | map(attribute='summary') | list)
| list
}}
sequence:
- target:
entity_id: todo.mealie_wocheneinkauf
data:
item: "{{ repeat.item.summary }}"
status: completed
action: todo.update_item
- alias: Reset re-added items
repeat:
for_each: |-
{{
source_items['todo.einkaufen']['items']
| selectattr('status', 'eq', 'needs_action')
| selectattr('summary', 'in', existing_items['todo.mealie_wocheneinkauf']['items'] | selectattr('status', 'eq', 'completed') | map(attribute='summary') | list)
| list
}}
sequence:
- target:
entity_id: todo.mealie_wocheneinkauf
data:
item: "{{ repeat.item.summary }}"
status: needs_action
action: todo.update_item
alias: Mealie | Sync To-Do List | Bring! List
description: ""
icon: mdi:food-fork-drink
Script: Update Mealie List
sequence:
- alias: Update list | Mealie
sequence:
- alias: Retrieve existing entries from Mealie list
target:
entity_id: todo.mealie_wocheneinkauf
data:
status:
- needs_action
- completed
response_variable: source_items
action: todo.get_items
- alias: Retrieve existing entries from Bring! list
target:
entity_id: todo.einkaufen
data:
status:
- needs_action
- completed
response_variable: existing_items
action: todo.get_items
- alias: Add new entries to Bring! list
repeat:
for_each: |-
{{
source_items['todo.mealie_wocheneinkauf']['items']
| selectattr('status', 'eq', 'needs_action')
| rejectattr('summary', 'in', existing_items['todo.einkaufen']['items'] | map(attribute='summary') | list)
| list
}}
sequence:
- target:
entity_id: todo.einkaufen
data:
item: "{{ repeat.item.summary }}"
action: todo.add_item
- alias: Mark completed items as done in Bring!
repeat:
for_each: |-
{{
source_items['todo.mealie_wocheneinkauf']['items']
| selectattr('status', 'eq', 'completed')
| selectattr('summary', 'in', existing_items['todo.einkaufen']['items'] | selectattr('status', 'ne', 'completed') | map(attribute='summary') | list)
| list
}}
sequence:
- target:
entity_id: todo.einkaufen
data:
item: "{{ repeat.item.summary }}"
status: completed
action: todo.update_item
- alias: Reset re-added items
repeat:
for_each: |-
{{
source_items['todo.mealie_wocheneinkauf']['items']
| selectattr('status', 'eq', 'needs_action')
| selectattr('summary', 'in', existing_items['todo.einkaufen']['items'] | selectattr('status', 'eq', 'completed') | map(attribute='summary') | list)
| list
}}
sequence:
- target:
entity_id: todo.einkaufen
data:
item: "{{ repeat.item.summary }}"
status: needs_action
action: todo.update_item
alias: Mealie | Sync To-Do List | Mealie List
description: ""
icon: mdi:food-fork-drink