NEWS
Todoist Script Lösung
-
Moin Moin,
der Adapter war unschlagbar. Ich hab nicht weiter geguckt, ob sich jemand dem aktuell angenommen hat, da zur Zeit Warn-und Errormeldungen kommen , wenn ich den Adapter starte.
Eine Suche ergab, dass Todoist mal wieder die API geändert hat. In den Adapter Dateien fummeln hat nichts gebracht. Also Old School per Skript und KI Hilfe.
Ich benutze Todoist schon seit Ewigkeiten und möchte auf die Vielfalt nicht verzichten.
In der App eingetragen erscheint es fast in Echtzeit in der VIS. Oder Alexa liest die Liste vor.
Next Step wäre , dass Alexa die Liste durch Zuruf befüllt.
Aber erstmal das Skript (meine Einkaufsliste). Token und Projektnummer gibs in der App.const TOKEN = "XXXXXX";
const PROJECT_ID = "XXXXXXX";
const DP_HTML = "0_userdata.0.todoist.einkaufsliste.html";
const DP_COUNT = "0_userdata.0.todoist.einkaufsliste.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("*/4 * * * ", 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 => "• " + escapeHtml(t.content || "")).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, ">");
}VIS Beispielbild

-
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.
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