<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Klipper --&gt; Telegram --&gt; Snapshot]]></title><description><![CDATA[<p dir="auto"><code>wurde mit Hilfe von KI (Perplexity) erstellt</code></p>
<p dir="auto"></p><section class="spoiler-wrapper"><button class="spoiler-control btn btn-default"><strong>--&gt; Script</strong></button><section style="display:none" class="spoiler-content"><p></p>
<pre><code>/**
 * ========================================================
 * 3D-Druck Telegram Snapshot Script
 * ========================================================
 * 
 * Überwacht den Status deines 3D-Druckers und sendet per Telegram
 * regelmäßig Snapshots deiner Drucker-Webcam mit Fortschrittsbalken,
 * Restzeit und Dateinamen.
 * 
 * Version:     2.1 (2026-03-25)
 * Autor:       Christian Wimmer
 * ioBroker:    JavaScript-Adapter
 * 
 * LIZENZ: MIT License
 * 
 * Copyright (c) 2026 Christian Wimmer
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 * ========================================================
 */

console.log("🖨️ 3D-Druck Telegram Script v2.1 gestartet");

// ==========================
// KONFIGURATION
// ==========================

const TELEGRAM_INSTANCE = "telegram.0";
const CHAT_ID           = DEINE_CHAT_ID;

const DP_STATE     = "0_userdata.0.3DDrucker.Snapmaker_U1.print_stats.state";
const DP_PROGRESS  = "0_userdata.0.3DDrucker.Snapmaker_U1.virtual_sdcard.progress_percent";
const DP_FILENAME  = "0_userdata.0.3DDrucker.Snapmaker_U1.print_stats.filename";
const DP_REMAIN    = "0_userdata.0.3DDrucker.Snapmaker_U1.Restzeit";

const SNAPSHOT_URL = "http://10.0.1.244/webcam/snapshot.jpg";
const DP_LAST_STEP = "0_userdata.0.3DDrucker.Script.lastStep";

const STEP_SIZE    = 10;
const SNAP_TIMEOUT = 5000;
const MAX_STEP     = Math.floor(100 / STEP_SIZE);

// ==========================
// VARIABLEN
// ==========================

let printingActive = false;
let currentFile    = "";

// ==========================
// INIT STATE
// ==========================

createState(DP_LAST_STEP, -1, {
    type:  "number",
    read:  true,
    write: true,
    def:   -1
});

// ==========================
// HELFER
// ==========================

function sendTgText(text) {
    if (!text) return;
    sendTo(TELEGRAM_INSTANCE, "send", {
        text,
        chatId: CHAT_ID
    });
}

function getFileName() {
    const raw = getState(DP_FILENAME)?.val;
    if (!raw || typeof raw !== "string") return "Unbekannt";

    const withoutPath = raw.includes("/") ? raw.split("/").pop() : raw;
    return withoutPath.replace(/\.gcode$/i, "");
}

function formatTime(seconds) {
    const s = Number(seconds);
    if (!Number.isFinite(s) || s &lt;= 0) return "--:--:--";

    const h   = Math.floor(s / 3600);
    const m   = Math.floor((s % 3600) / 60);
    const sec = s % 60;

    return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`;
}

function getBar(p) {
    const progress = Math.min(Math.max(Number(p) || 0, 0), 100);
    const total    = 20;
    const filled   = Math.round((progress / 100) * total);
    let out        = "";

    for (let i = 0; i &lt; total; i++) {
        if (i &lt; filled) {
            if (progress &lt; 30) out += "🟥";
            else if (progress &lt; 70) out += "🟨";
            else out += "🟩";
        } else {
            out += "⬜";
        }
    }
    return out;
}

function buildCaption(progress) {
    const p      = Math.min(Math.max(Number(progress) || 0, 0), 100);
    const remain = getState(DP_REMAIN)?.val;

    return (
`📸 ${p}%
${getBar(p)}
⏱️ ${formatTime(remain)}
📄 ${currentFile || "Unbekannt"}`
    );
}

// ==========================
// SNAPSHOT
// ==========================

function sendSnapshot(progress) {
    const caption = buildCaption(progress);

    httpGet(
        SNAPSHOT_URL,
        { responseType: "arraybuffer", timeout: SNAP_TIMEOUT },
        (err, res) =&gt; {
            const hasImage = !err &amp;&amp; res &amp;&amp; res.data &amp;&amp; res.data.byteLength &gt; 1000;

            if (!hasImage) {
                sendTgText(caption + "\n⚠️ Kein Bild");
                return;
            }

            const buffer = Buffer.from(new Uint8Array(res.data));

            writeFile("0_userdata.0", "snapshot_3d.jpg", buffer, () =&gt; {
                readFile("0_userdata.0", "snapshot_3d.jpg", (error, data) =&gt; {
                    if (error || !data) {
                        sendTgText(caption + "\n⚠️ Lesefehler");
                        return;
                    }

                    sendTo(TELEGRAM_INSTANCE, "send", {
                        text:   data,
                        type:   "photo",
                        caption,
                        chatId: CHAT_ID
                    });
                });
            });
        }
    );
}

// ==========================
// START / STOP
// ==========================

on({ id: DP_STATE, change: "ne" }, obj =&gt; {
    const state = obj?.state?.val;

    if (state === "printing") {
        printingActive = true;
        currentFile    = getFileName();

        const p    = Math.floor(Number(getState(DP_PROGRESS)?.val) || 0);
        const step = Math.floor(p / STEP_SIZE);

        setState(DP_LAST_STEP, step, true);

        sendTgText(`🖨️ Start\n📄 ${currentFile}\n📊 ${p}%`);

        setTimeout(() =&gt; sendSnapshot(p), 1500);
        return;
    }

    const isEndState = state === "complete" || state === "idle" || state === "error";

    if (isEndState &amp;&amp; printingActive) {
        printingActive = false;

        sendTgText(`🏁 Ende\n📄 ${currentFile}\nStatus: ${state}`);

        setTimeout(() =&gt; sendSnapshot(100), 2000);

        setState(DP_LAST_STEP, -1, true);
    }
});

// ==========================
// FORTSCHRITT
// ==========================

on({ id: DP_PROGRESS, change: "ne" }, obj =&gt; {
    if (!printingActive) return;

    const pRaw = Number(obj?.state?.val) || 0;
    const p    = Math.min(Math.max(Math.floor(pRaw), 0), 100);

    let last = Number(getState(DP_LAST_STEP)?.val);
    if (!Number.isFinite(last)) last = -1;

    while (p &gt;= (last + 1) * STEP_SIZE &amp;&amp; last &lt; MAX_STEP) {
        last++;
        setState(DP_LAST_STEP, last, true);
        sendSnapshot(p);
    }
});

// ==========================
// INIT (RESTART)
// ==========================

setTimeout(() =&gt; {
    const state = getState(DP_STATE)?.val;

    if (state !== "printing") return;

    printingActive = true;
    currentFile    = getFileName();

    const p    = Math.floor(Number(getState(DP_PROGRESS)?.val) || 0);
    const step = Math.floor(p / STEP_SIZE);

    setState(DP_LAST_STEP, step, true);

    sendTgText(`🔄 Restart erkannt\n📄 ${currentFile}\n📊 ${p}%`);

    setTimeout(() =&gt; sendSnapshot(p), 2000);
}, 5000);

console.log("🖨️ 3D-Druck Telegram Script v2.1 bereit");
</code></pre>
<p dir="auto"></p></section></section><p></p>
<h1><a href="https://github.com/Negalein/ioBroker_Klipper_Druckfortschritt_Telegram" rel="nofollow ugc">3D-Druck Telegram Snapshot Script</a></h1>
<p dir="auto">Dieses Projekt ist ein ioBroker-JavaScript, das den Status deines 3D-Druckers überwacht und per Telegram regelmäßig Snapshots deiner Drucker-Webcam mit Fortschrittsbalken, Restzeit und Dateinamen sendet.</p>
<hr />
<h2>Features</h2>
<ul>
<li>Start-/Ende-Benachrichtigung bei Druckjobs</li>
<li>Fortschrittsupdates in konfigurierbaren Schritten (z.B. alle 10 %)</li>
<li>Snapshot der Webcam als Foto in Telegram</li>
<li>Emoji-Fortschrittsbalken und formatierte Restzeit</li>
<li>Robust gegenüber ioBroker-/Adapter-Neustarts (Erkennung laufender Drucke)</li>
</ul>
<hr />
<h2>Voraussetzungen</h2>
<ul>
<li>ioBroker mit JavaScript-Adapter</li>
<li>Telegram-Adapter eingerichtet (Bot + Chat-ID bekannt)</li>
<li>3D-Drucker integriert (z.B. Snapmaker) mit folgenden Datenpunkten:
<ul>
<li>Druckstatus (<code>printing</code>, <code>complete</code>, <code>idle</code>, <code>error</code>)</li>
<li>Fortschritt in Prozent</li>
<li>Dateiname des Jobs</li>
<li>Restzeit (Sekunden oder kompatibel)</li>
</ul>
</li>
<li>Webcam-URL, die ein Snapshot-Bild liefert</li>
</ul>
<hr />
<h2>Installation</h2>
<ol>
<li>
<p dir="auto"><strong>Script anlegen</strong></p>
<ul>
<li>In ioBroker Admin zu „Skripte“ wechseln.</li>
<li>Neues Skript im JavaScript-Adapter erstellen.</li>
<li>Den kompletten Script-Code aus diesem Repository einfügen.</li>
</ul>
</li>
<li>
<p dir="auto"><strong>Konfiguration anpassen</strong></p>
<ul>
<li>Oben im Skript die Konstanten anpassen:
<ul>
<li><code>TELEGRAM_INSTANCE</code> (z.B. <code>"telegram.0"</code>)</li>
<li><code>CHAT_ID</code> (deine Chat-ID)</li>
<li><code>DP_STATE</code>, <code>DP_PROGRESS</code>, <code>DP_FILENAME</code>, <code>DP_REMAIN</code> auf deine Datenpunkte</li>
<li><code>SNAPSHOT_URL</code> auf deine Webcam-Snapshot-URL</li>
<li>Optional: <code>STEP_SIZE</code> (z.B. 5 oder 10)</li>
</ul>
</li>
</ul>
</li>
<li>
<p dir="auto"><strong>Speichern und aktivieren</strong></p>
<ul>
<li>Skript speichern.</li>
<li>Sicherstellen, dass das Skript aktiviert ist.</li>
</ul>
</li>
</ol>
<hr />
<h2>Kurzes Tutorial</h2>
<h3>1. Telegram-Chat-ID ermitteln</h3>
<ol>
<li>Telegram-Bot im Adapter anlegen und verbinden.</li>
<li>Eine Nachricht an deinen Bot senden.</li>
<li>Im ioBroker-Log oder in den Objekten des Telegram-Adapters nachsehen, welche Chat-ID verwendet wurde.</li>
<li>Diese ID in <code>CHAT_ID</code> im Skript eintragen.</li>
</ol>
<h3>2. Datenpunkte des Druckers finden</h3>
<ol>
<li>Im ioBroker-Admin unter „Objekte“ nach deinem Drucker-Adapter suchen (z.B. <code>Snapmaker_U1</code>).</li>
<li>Die relevanten Datenpunkte identifizieren:
<ul>
<li>Status (String, z.B. „printing“)</li>
<li>Fortschritt (Zahl in Prozent)</li>
<li>Dateiname (String)</li>
<li>Restzeit (Sekunden)</li>
</ul>
</li>
<li>Die vollständigen Pfade in die Konstanten <code>DP_STATE</code>, <code>DP_PROGRESS</code>, <code>DP_FILENAME</code>, <code>DP_REMAIN</code> im Skript eintragen.</li>
</ol>
<h3>3. Webcam-Snapshot testen</h3>
<ol>
<li>Die URL aus <code>SNAPSHOT_URL</code> im Browser öffnen.</li>
<li>Wenn ein Bild geladen wird, ist alles gut.</li>
<li>Falls nicht, die richtige Snapshot-URL des Druckers/Webcams ermitteln und im Skript eintragen.</li>
</ol>
<h3>4. Funktion testen</h3>
<ol>
<li>Einen Druckjob starten.</li>
<li>In Telegram solltest du eine Start-Nachricht mit Fortschritt und kurz danach ein Foto erhalten.</li>
<li>Während des Drucks bekommst du alle <code>STEP_SIZE</code> Prozent ein neues Bild.</li>
<li>Am Ende des Drucks kommt eine End-Nachricht + abschließender Snapshot.</li>
</ol>
<hr />
<h2>Konfigurationstipps</h2>
<ul>
<li><strong>STEP_SIZE verkleinern</strong> (z.B. 5), um häufiger Bilder zu bekommen.</li>
<li><strong>SNAP_TIMEOUT</strong> erhöhen, wenn deine Webcam regelmäßig länger braucht.</li>
<li>Falls du mehrere Drucker hast, kannst du das Skript kopieren und die Datenpunkte/URLs pro Drucker anpassen.</li>
</ul>
<hr />
<h2>Lizenz</h2>
<p dir="auto">Dieses Projekt steht unter der <strong>MIT License</strong>.</p>
<pre><code></code></pre>
]]></description><link>https://forum.iobroker.net/topic/84299/klipper-telegram-snapshot</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 01:30:07 GMT</lastBuildDate><atom:link href="https://forum.iobroker.net/topic/84299.rss" rel="self" type="application/rss+xml"/><pubDate>Mon, 13 Apr 2026 21:36:18 GMT</pubDate><ttl>60</ttl></channel></rss>