Update:
Mit diesem Befehl in einer Konsole kann man seine Projekte auflisten lassen (aus der App).
Das ist wichtig für die Projekt ID im Skript.
curl -i https://api.todoist.com/api/v1/projects
-H "Authorization: Bearer XXXXXXXX" (für die XXXX muss euer Token eingesetzt werden)
Die Ausgabe sähe als Beispiel so aus:
{
"results": [
{
"id": "XXXXXX",
"can_assign_tasks": false,
"can_comment": true,
"child_order": 0,
"is_collapsed": false,
"color": "grey",
"creator_uid": "",
"created_at": "",
"is_archived": false,
"is_deleted": false,
"is_favorite": false,
"is_frozen": false,
"name": "Inbox",
"is_shared": false,
"updated_at": "",
"view_style": "list",
"default_order": 0,
"description": "",
"public_access": false,
"public_key": "",
"access": {
"visibility": "restricted",
"configuration": {}
},
"role": "CREATOR",
"goal_ids": [],
"parent_id": null,
"inbox_project": true
},
{
"id": "XXXXXX",
"can_assign_tasks": false,
"can_comment": true,
"child_order": 1,
"is_collapsed": false,
"color": "sky_blue",
"creator_uid": "",
"created_at": "",
"is_archived": false,
"is_deleted": false,
"is_favorite": false,
"is_frozen": false,
"name": "Alexa-Einkaufsliste",
"is_shared": false,
"updated_at": "",
"view_style": "list",
"default_order": 0,
"description": "",
"public_access": false,
"public_key": "",
"access": {
"visibility": "restricted",
"configuration": {}
},
"role": "CREATOR",
"goal_ids": [],
"parent_id": null,
"inbox_project": false
},
{
"id": "XXXXXX",
"can_assign_tasks": false,
"can_comment": true,
"child_order": 2,
"is_collapsed": false,
"color": "orange",
"creator_uid": "",
"created_at": "",
"is_archived": false,
"is_deleted": false,
"is_favorite": false,
"is_frozen": false,
"name": "Alexa To-Do Liste",
"is_shared": false,
"updated_at": "",
"view_style": "list",
"default_order": 0,
"description": "",
"public_access": false,
"public_key": "",
"access": {
"visibility": "restricted",
"configuration": {}
},
"role": "CREATOR",
"goal_ids": [],
"parent_id": null,
"inbox_project": false
}
],
"next_cursor": null
Nehmen wir den Block , wo Alexa-Einkaufsliste steht. Darüber gibt es das Feld "id".
Diese ID nehmt ihr für euer Skript.
Das könnt ihr für jedes Projekt , was ihr in der App angelegt habt, machen.
Ich habe ne ToDo Liste noch mit Aufgaben, die ich mir zukünftig so vorgenommen habe.
Ein HTML Widget und sie werden mir in der VIS angezeigt.
Immer wenns mir in den Kopp kommt, trage ich ne Aufgabe oder was für den Einkauf in der ToDoIst App ein und je nach Update Intervall im Skript, erscheint es dann in der VIS.
[image: 1778806962575-4af9418b-b376-4f21-8e11-192aec03c340-image.jpeg]
Token und Project ID vervollständigen und die beiden Datenpunkte anlegen
0_userdata.0.todoist.aufgaben.html
0_userdata.0.todoist.aufgaben.count
const TOKEN = "XXXXXX";
// 🔴 Kann numerisch sein (muss nicht)
const PROJECT_ID = "XXXXXX";
const DP_HTML = "0_userdata.0.todoist.aufgaben.html";
const DP_COUNT = "0_userdata.0.todoist.aufgaben.count";
// Cache
let lastHTML = "Keine Daten";
let lastCount = 0;
// =====================
// LOG INFO TEIL (SEPARAT & CLEAN)
// =====================
function logInfo(msg) {
log("[TODOIST] " + msg, "info");
}
function logError(msg) {
log("[TODOIST ERROR] " + msg, "info"); // kein warn/error → keine Stacktraces
}
// =====================
// START
// =====================
schedule("*/5 * * * *", loadTasks);
loadTasks();
// =====================
// MAIN
// =====================
function loadTasks() {
logInfo("Lade Tasks...");
httpPost("https://api.todoist.com/api/v1/sync",
"sync_token=*&resource_types=" + encodeURIComponent('["items"]'),
{
headers: {
"Authorization": "Bearer " + TOKEN,
"Content-Type": "application/x-www-form-urlencoded"
},
timeout: 10000
},
(err, res) => {
if (err) {
logError("HTTP Fehler");
setState(DP_HTML, lastHTML, true);
setState(DP_COUNT, lastCount, true);
return;
}
let data;
try {
data = JSON.parse(res.data);
} catch (e) {
logError("JSON Fehler");
return;
}
const items = data.items || [];
if (!Array.isArray(items)) return;
const filtered = items.filter(t =>
t.project_id == PROJECT_ID
);
const html = filtered.length
? filtered.map(t => {
let text = escapeHtml(t.content || "");
// entfernt führende "-" oder "•" falls sie im Task stehen
text = text.replace(/^(\s*[-•]\s*)+/, "");
return "• " + text;
}).join("<br>")
: "Keine Einträge vorhanden";
setState(DP_HTML, html, true);
setState(DP_COUNT, filtered.length, true);
lastHTML = html;
lastCount = filtered.length;
logInfo("OK: " + filtered.length + " Items");
}
);
}
// =====================
// HTML SAFE
// =====================
function escapeHtml(str) {
return (str || "")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}