What does this setup do?
Sync a list from Todoist to the Home Assistant shopping list component. If you sync Alexa to Todoist, you can have Home Assistant and Alexa share the contents of the shopping list.
This script provides a service to sync the HA shopping list with the Todoist list. The Todoist list will overwrite the one stored by HA. When an item is checked or added to the HA list, it will be updated on the Todoist list. Optionally if you setup webhooks from Todoist, you can have HA sync the shopping list automatically whenever something changes.
Requirements
- pyscript custom-components/pyscript: Pyscript adds rich Python scripting to HASS (github.com) - enable the global HASS variable when setting up
- Todoist API key (Integrations (todoist.com))
- Todoist project ID (when on your Alexa Shopping List project, use the ID at the end of the URL)
Add this file to your config/pyscript/shopping_list_sync.py
import requests
import sys
import time
from importlib import reload
if "/config/pyscript_modules" not in sys.path:
sys.path.append("/config/pyscript_modules")
import write_file
write_file = reload(write_file)
TODOIST_TOKEN = "<your Todoist API token>"
TODOIST_PROJECT_ID = <your Todoist project ID>
def get_tasks():
get_tasks_url =f"https://api.todoist.com/rest/v1/tasks?project_id={TODOIST_PROJECT_ID}"
headers = {"Authorization" : f"Bearer {TODOIST_TOKEN}"}
status_code = 500
while status_code is not 200:
response = task.executor(requests.get, get_tasks_url, headers = headers)
status_code = response.status_code
time.sleep(1)
json = response.json()
return json
def add_task(item):
url = "https://api.todoist.com/rest/v1/tasks"
headers = {"Authorization" : f"Bearer {TODOIST_TOKEN}", "Content-Type" : "application/json"}
body = {"content" : item, "project_id" : TODOIST_PROJECT_ID}
status_code = 500
while status_code is not 200:
response = task.executor(requests.post, url, headers = headers, json=body)
status_code = response.status_code
time.sleep(1)
if status_code == 200:
return True
else:
return False
def update_task(id, content):
url = f"https://api.todoist.com/rest/v1/tasks/{id}"
headers = {"Authorization" : f"Bearer {TODOIST_TOKEN}", "Content-Type" : "application/json"}
body = {"content" : content}
status_code = 500
counter = 0
while status_code is not 204 and counter < 10:
response = task.executor(requests.post, url, headers = headers, json=body)
status_code = response.status_code
time.sleep(1)
counter += 1
if status_code == 204:
return True
else:
return False
def complete_task(id):
url = f"https://api.todoist.com/rest/v1/tasks/{id}/close"
headers = {"Authorization" : f"Bearer {TODOIST_TOKEN}"}
status_code = 500
counter = 0
while status_code is not 204 and counter < 10:
response = task.executor(requests.post, url, headers = headers)
status_code = response.status_code
time.sleep(1)
counter += 1
if status_code == 204:
return True
else:
return False
@service
def sync_shopping_list():
tasks = []
json = get_tasks()
for item in json:
tasks.append({"name" : item["content"], "id" : str(item["id"]), "complete" : item["completed"]})
task.executor(write_file.write_json, filename = "/config/.shopping_list.json", content=tasks)
hass.data["shopping_list"].async_load()
@event_trigger('shopping_list_updated')
def update_shopping_list(action=None, item=None):
if action == "add":
add_task(item["name"])
sync_shopping_list()
if action == "update" and item["complete"] == False:
update_task(item["id"],item["name"])
sync_shopping_list()
if action == "update" and item["complete"] == True:
complete_task(item["id"])
sync_shopping_list()
Add this file to config/pyscript_modules/write_file.py
import json
def write_json(filename, content):
with open(filename, 'w') as file:
json.dump(content, file, indent=4)
Restart pyscript
Run the service pyscript.sync_shopping_list
from developer tools.
You should now have a synced list. Note this will remove everything you have in the current shopping list, so add the items to Todoist first.
Setting up webhook to update HA when Todoist is updated (via Alexa etc.)
For this to work, you need to have your HA available on the Internet with SSL enabled.
-
Set up an automation in HA with a webhook trigger. Set the action to call service and call
pyscript.sync_shopping_list
-
Create a new app in Todoist - https://developer.todoist.com/appconsole.html
-
Choose a name, and then scroll down to Webhooks.
-
Under Watched events, select all item:* options (added, deleted etc.)
-
Enter the webhook URL in the following format:
https://<your-HA-URL>/api/webhook/<webhook-id-from-automation>
-
Scroll down and click the “Create test token” button:
-
Head to integrations and check your app name is listed.
-
Restart Home Assistant.
-
Now when you update anything on Todoist, the webhook should be called to get HA to update the shopping list!