NEWS
Todoist Script Lösung
-
DANKE füs Teilen.
Bisher hat kein User an einem Versuch der Migration des Adapter interesse gezeigt:
https://forum.iobroker.net/topic/84408/deprecated-adapter-todoist2 -
Aber wenn das eh per Script geht stellt sich die Frage ob irgendein Aufwand für den Adapter sinnvoll wäre. Vor allem wenn es kaum Interesse gibt.
Der github user stroell hat gestern mit copilot was am fork gemacht
https://github.com/Stroell/ioBroker.todoist2/commits/master/ist wohl der user @stroell (forum/github gleiches Bild)
war aber seit der Anmeldung 2023 nicht mehr im Forum -
Das wird ihm nur privat was nutzen, da das Paket unter dem Namen nicht veröffentlicht werden kann.
-
Fortsetzung:
Ansage von Alexa, was auf der Einkaufsliste steht.
Eine Routine anlegen mit , wenn gesagt wird einkaufsliste, dann soll Alexa benutzerdefiniert anworten (was, könnt ihr euch aussuchen).
Hat den Hintergrund, dass Alexa auf das Wort "einkaufsliste" reagiert und nicht eine Fehlermeldung raushaut.
Dann ein Blockly anlegen:
Damit triggern wir auf das Wort einkaufsliste (Kleinschreibung beachten)
Den DP setzen wir , wenn das Wort erkannt wird, kurz auf true (dadurch wird das Script ausgelöst) und 2 sek später wieder auf false.
Der DP wird vom Skript angelegt.
Korrektur.
Den müsst ihr selber anlegen.

const TOKEN = "XXXXXXXXXXXX"; // 🔴 numerische Projekt-ID const PROJECT_ID = "XXXXXXXX"; // 🔵 TRIGGER const TRIGGER_DP = "0_userdata.0.todoist.readList"; let isRunning = false; // ===================== // TRIGGER // ===================== on({ id: TRIGGER_DP, change: "ne" }, (obj) => { if (obj.state.val !== true) return; if (isRunning) return; isRunning = true; loadTasks(); // Trigger sofort zurücksetzen (kein Loop!) setTimeout(() => { setState(TRIGGER_DP, false, true); isRunning = false; }, 1500); }); // ===================== // MAIN // ===================== function loadTasks() { 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) { speak("Ich konnte deine Einkaufsliste nicht laden."); return; } let data; try { data = JSON.parse(res.data); } catch (e) { speak("Fehler beim Verarbeiten der Daten."); return; } const items = data.items || []; const list = items.filter(t => t.project_id == PROJECT_ID ); if (!list.length) { speak("Deine Einkaufsliste ist leer."); return; } let text; if (list.length === 1) { text = "Du hast nur " + list[0].content + " auf deiner Einkaufsliste."; } else if (list.length <= 5) { text = "Auf deiner Einkaufsliste stehen: " + list.map(t => t.content).join(", "); } else { text = "Du hast " + list.length + " Artikel. Zum Beispiel: " + list.slice(0, 5).map(t => t.content).join(", "); } speak(text); } ); } // ===================== // 🔊 SPEAK (DEIN FIX BEIBEHALTEN) // ===================== function speak(text) { if (!text) return; const dp = "alexa2.0.Echo-Devices.XXXXXXXXXXXXX.Commands.speak"; if (!existsState(dp)) { log("Alexa speak DP nicht gefunden!", "error"); return; } log("ALEXA SPEAK: " + text, "info"); setState(dp, ""); setTimeout(() => { setState(dp, text + " "); }, 1000); }Token und Project ID findet ihr in eurem Todoist Account.
Natürlich solltest ihr vorher eine Einkaufsliste angelegt haben (Projekt).
Den Echo bzw. die Seriennummer bekommt ihr aus dem Alexa2 Adapter.const dp =
"alexa2.0.Echo-Devices.XXXXXXXXXXXXX.Commands.speak"Im Skript selber, ich bin da Laie, steht wohl, dass der DP nachdem er auf true gegangen ist, gleich wieder auf false gesetzt wird.
Bei mir hat es nun so, wie ich es hier aufgeschrieben habe funktioniert.
Ihr könnt ja ein wenig rumprobieren.Was aktuell nicht so einfach funktioniert, ist, Sachen per Sprache auf die Einkaufsliste zu setzen.
Da habe ich mir bis jetzt die Zähne ausgebissen.Edit: kleiner Hinweis zur summary. Das Wort muss ich sich ja ändern in dem DP. Also 2 mal hintereinander einkaufsliste sagen, löst nur 1 mal das Skript aus.
Also nach dem Wort einkaufsliste nach der Uhrzeit fragen , damit sich der DP wieder ändert
Edit 2: Es funktioniert wohl auch, wenn man beim Trigger aktualisiert nimmt. Da kann man so oft einkaufsliste sagen , wie man will.
-
Aber wenn das eh per Script geht stellt sich die Frage ob irgendein Aufwand für den Adapter sinnvoll wäre. Vor allem wenn es kaum Interesse gibt.
-
@Stadtschloss
Das Cross-Posting hier ist völlig sinnfrei, da es hier nicht um den Adapter selbst geht (der Dir jetzt vermeintlich fehlt), sondern um einen zumindest teilweisen Ersatz. Dein Interesse am eigentlichen Adapter hast Du ja bereits im (abgekündigten) Thread gepostet. -
@Stadtschloss
Das Cross-Posting hier ist völlig sinnfrei, da es hier nicht um den Adapter selbst geht (der Dir jetzt vermeintlich fehlt), sondern um einen zumindest teilweisen Ersatz. Dein Interesse am eigentlichen Adapter hast Du ja bereits im (abgekündigten) Thread gepostet. -
Man könnte natürlich im Script, in der Funktion getTasks die Daten die von der api zurückkommen, optional auch in einen datenpunkt schreiben. Das wäre nicht so sehr komplex.
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": nullNehmen 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.
Token und Project ID vervollständigen und die beiden Datenpunkte anlegen
0_userdata.0.todoist.aufgaben.html
0_userdata.0.todoist.aufgaben.countconst 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, ">"); }
Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.
Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.
Mit deinem Input könnte dieser Beitrag noch besser werden 💗
Registrieren Anmelden