<?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[Dynamische HTML-Progress-Bar]]></title><description><![CDATA[<p dir="auto">Dieses Skript bietet eine elegante Lösung, um ohne aufwendige Widget-Konfigurationen voll anpassbare Fortschrittsbalken (horizontal oder vertikal) direkt per JavaScript zu generieren. Ideal für Dashboards, bei denen Füllstände, Batteriestände oder Sensorwerte grafisch ansprechend visualisiert werden sollen.</p>
<p dir="auto"><img src="/assets/uploads/files/1782905325385-76c66a4b-b1a0-4cc0-8494-04784944a4f8-image.jpeg" alt="76c66a4b-b1a0-4cc0-8494-04784944a4f8-image.jpeg" class=" img-fluid img-markdown" /></p>
<p dir="auto"><strong>Hinweis: Dieses Skript wurde mit Unterstützung von KI entwickelt, um eine effiziente und saubere Code-Struktur für die dynamische HTML-Generierung sicherzustellen.</strong></p>
<p dir="auto"><strong>Warum dieses Skript?</strong><br />
In der Visualisierung (VIS) stößt man bei vielen gleichartigen Elementen oft an Grenzen. Anstatt für jedes Widget manuell Einstellungen vorzunehmen, generiert dieses Skript den HTML-Code "on-the-fly".</p>
<p dir="auto"><strong>Einheitliches Design:</strong><br />
Alle Balken sind konsistent.</p>
<p dir="auto"><strong>Flexibel:</strong><br />
Einfache Anpassung von Farben, Größen und Stilen über Parameter.</p>
<p dir="auto"><strong>Performance:</strong><br />
Nutzt leichtgewichtiges Inline-HTML/CSS, daher sehr schnell in basic-html Widgets.</p>
<p dir="auto"><strong>Was ist nötig?</strong><br />
Du benötigst die Instanz javascript.</p>
<p dir="auto"><strong>Datenpunkt:</strong><br />
Erstelle einen Datenpunkt (Typ String) in <strong>0_userdata.0</strong>, in dem das generierte HTML gespeichert wird.</p>
<p dir="auto"><strong>Widget:</strong><br />
Erstelle in deiner VIS ein basic-html Widget und binde den Datenpunkt per Bindings ein: <strong>{0_userdata.0.dein.datenpunkt}</strong>.</p>
<p dir="auto"><strong>Die Parameter-Erklärung</strong></p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="text-align:left">Parameter</th>
<th style="text-align:left">Typ</th>
<th style="text-align:left">Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">orientation</td>
<td style="text-align:left">String</td>
<td style="text-align:left">"horizontal" oder "vertical"</td>
</tr>
<tr>
<td style="text-align:left">value</td>
<td style="text-align:left">Number</td>
<td style="text-align:left">Aktueller Wert in Prozent (0 - 100)</td>
</tr>
<tr>
<td style="text-align:left">progressColor</td>
<td style="text-align:left">String</td>
<td style="text-align:left">Farbe des Balkens (Hex, RGB oder RGBA)</td>
</tr>
<tr>
<td style="text-align:left">backgroundColor</td>
<td style="text-align:left">String</td>
<td style="text-align:left">Hintergrundfarbe (Hex, RGB, RGBA, "transparent")</td>
</tr>
<tr>
<td style="text-align:left">border</td>
<td style="text-align:left">Number</td>
<td style="text-align:left">Rahmenstärke in Pixeln</td>
</tr>
<tr>
<td style="text-align:left">borderColor</td>
<td style="text-align:left">String</td>
<td style="text-align:left">Rahmenfarbe (Hex, RGB oder RGBA)</td>
</tr>
<tr>
<td style="text-align:left">borderRadius</td>
<td style="text-align:left">Number</td>
<td style="text-align:left">Eckenabrundung in Pixeln</td>
</tr>
<tr>
<td style="text-align:left">labelFont</td>
<td style="text-align:left">String</td>
<td style="text-align:left">Schriftart (z.B. "Arial")</td>
</tr>
<tr>
<td style="text-align:left">labelSize</td>
<td style="text-align:left">String</td>
<td style="text-align:left">Schriftgröße (z.B. "20px") "0px" wenn das Label ausgeblendet werden soll</td>
</tr>
<tr>
<td style="text-align:left">labelColor</td>
<td style="text-align:left">String</td>
<td style="text-align:left">Textfarbe (Hex, RGB oder RGBA)</td>
</tr>
<tr>
<td style="text-align:left">labelshadow</td>
<td style="text-align:left">Boolean</td>
<td style="text-align:left">true für dezenten Text-Schatten</td>
</tr>
<tr>
<td style="text-align:left">labelsuffix</td>
<td style="text-align:left">String</td>
<td style="text-align:left">Anhang hinter dem Wert (z.B. "%")</td>
</tr>
<tr>
<td style="text-align:left">gradient</td>
<td style="text-align:left">Boolean</td>
<td style="text-align:left">true für automatischen Farbverlauf</td>
</tr>
<tr>
<td style="text-align:left">effect</td>
<td style="text-align:left">String</td>
<td style="text-align:left">"glanz" für Glas-Effekt oder "none"</td>
</tr>
<tr>
<td style="text-align:left">maxValue</td>
<td style="text-align:left">Number</td>
<td style="text-align:left">Standardwert = 100, kann aber auch jeder andere Wert sein</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>Farbangaben &amp; Transparenz</strong><br />
Die Funktion ist für Standard-CSS-Farben ausgelegt. Du kannst für alle Farb-Parameter (progressColor, backgroundColor, borderColor, labelColor) die folgenden Formate verwenden:</p>
<p dir="auto"><strong>Hex-Werte:</strong> (z. B. "#2a2a2a"). Wichtig: Für den Parameter gradient = true solltest du bevorzugt Hex-Werte verwenden, da die interne Logik für Farbverläufe darauf optimiert ist.</p>
<p dir="auto"><strong>RGB / RGBA:</strong> Du kannst Farben als rgb(r, g, b) oder mit Transparenz als rgba(r, g, b, alpha) definieren.</p>
<p dir="auto"><strong>Beispiel:</strong> "rgba(0, 128, 0, 0.5)" für ein halbtransparentes Grün.</p>
<p dir="auto"><strong>"transparent":</strong> Verwende das Schlüsselwort "transparent", um den Hintergrund komplett auszublenden.</p>
<h2>Hier das Script:</h2>
<pre><code>// === Dynamische HTML-Progress-Bar ===
 
//Erst-Version 1.0.0 - 01.07.2026
//Version 1.0.1 - 01.07.2026
//Ersteller Ro75.
 
//Voraussetzungen (Version 1.0.1 getestet mit)
//NodeJS: 22.22.x
//Javascript-Adapter: 9.0.18
//Admin-Adapter: 7.8.23
//js-controler: 7.2.2

function buildProgressBar(orientation, value, progressColor, backgroundColor, border, borderColor, borderRadius, labelFont, labelSize, labelColor, labelshadow = false, labelsuffix, gradient, effect, maxValue = 100) {
    function adjustColor(col, amt) {
        if (col[0] === "#") {
            let hex = col.slice(1);
            if (hex.length === 3) {
                hex = hex[0]+hex[0] + hex[1]+hex[1] + hex[2]+hex[2];
            }
            let num = parseInt(hex, 16);
            let r = (num &gt;&gt; 16) + amt;
            let g = ((num &gt;&gt; 8) &amp; 0xFF) + amt;
            let b = (num &amp; 0xFF) + amt;
            r = Math.max(0, Math.min(255, r));
            g = Math.max(0, Math.min(255, g));
            b = Math.max(0, Math.min(255, b));
            return "#" + ((r &lt;&lt; 16) | (g &lt;&lt; 8) | b).toString(16).padStart(6, "0");
        }

        if (col.startsWith("rgb")) {
            let nums = col.replace(/[^\d.,]/g, "").split(",");
            let r = parseInt(nums[0]) + amt;
            let g = parseInt(nums[1]) + amt;
            let b = parseInt(nums[2]) + amt;
            let a = nums.length === 4 ? parseFloat(nums[3]) : null;

            r = Math.max(0, Math.min(255, r));
            g = Math.max(0, Math.min(255, g));
            b = Math.max(0, Math.min(255, b));

            if (a !== null) {
                return `rgba(${r},${g},${b},${a})`;
            } else {
                return `rgb(${r},${g},${b})`;
            }
        }
        return col;
    }

    const percent = (maxValue === 0) ? 0 : (value / maxValue) * 100;
    let finalProgressColor = progressColor;

    if (gradient === true) {
        const dark = adjustColor(progressColor, -40);
        const light = adjustColor(progressColor, +60);

        if (orientation === "horizontal") {
            finalProgressColor = `linear-gradient(to right, ${dark} 0%, ${progressColor} 50%, ${light} 100%)`;
        } else {
            finalProgressColor = `linear-gradient(to top, ${dark} 0%, ${progressColor} 50%, ${light} 100%)`;
        }
    }

    let glanz = "";
    if (effect === "glanz") {
        if (orientation === "horizontal") {
            glanz = `&lt;div style="position:absolute;left:0;top:0;width:100%;height:45%;background:rgba(255,255,255,0.35);"&gt;&lt;/div&gt;`;
        } else {
            glanz = `&lt;div style="position:absolute;left:0;top:0;width:45%;height:100%;background:rgba(255,255,255,0.35);"&gt;&lt;/div&gt;`;
        }
    }

    let labelShadowStyle = "";
    if (labelshadow === true) {
        labelShadowStyle = "text-shadow:1px 1px 2px black;";
    }

    let finalLabelText = value + " " + labelsuffix;

    if (orientation === "horizontal") {
        return `
            &lt;div style="width:100%;height:100%;background:${backgroundColor};border:${border}px solid ${borderColor};border-radius:${borderRadius}px;box-sizing:border-box;position:relative;overflow:hidden;"&gt;
                &lt;div style="width:${percent}%;height:100%;background:${finalProgressColor};position:relative;"&gt;
                    ${glanz}
                &lt;/div&gt;
                &lt;div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:100%;text-align:center;color:${labelColor};font-family:${labelFont};font-size:${labelSize};font-weight:bold;${labelShadowStyle}pointer-events:none;"&gt;
                    ${finalLabelText}
                &lt;/div&gt;
            &lt;/div&gt;
        `;
    } else {
        return `
            &lt;div style="width:100%;height:100%;background:${backgroundColor};border:${border}px solid ${borderColor};border-radius:${borderRadius}px;display:flex;align-items:flex-end;box-sizing:border-box;position:relative;overflow:hidden;"&gt;
                &lt;div style="width:100%;height:${percent}%;background:${finalProgressColor};position:relative;"&gt;
                    ${glanz}
                &lt;/div&gt;
                &lt;div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-90deg);font-family:${labelFont};font-size:${labelSize};font-weight:bold;color:${labelColor};${labelShadowStyle}pointer-events:none;white-space:nowrap;"&gt;
                    ${finalLabelText}
                &lt;/div&gt;
            &lt;/div&gt;
        `;
    }
}
</code></pre>
<p dir="auto"><strong>Anwendung mit on()-Trigger (Automatische Aktualisierung):</strong><br />
Anstatt den Wert manuell zu setzen, solltest du das Skript durch einen Trigger auslösen lassen. So aktualisiert sich deine ProgressBar automatisch, sobald sich der Wert deines Sensors ändert:</p>
<pre><code>// Beispiel: Überwache einen Datenpunkt (z.B. einen Füllstand)
on({ id: 'alias.0.Geräte.Level', change: 'any' }, function (obj) {
    
    // Die ProgressBar wird bei jeder Änderung des Datenpunktes neu berechnet
    let barHTML = buildProgressBar(
        "horizontal", 
        obj.state.val,              // Der aktuelle Wert vom Sensor
        "#2a2a2a",                  // progressColor
        "transparent",              // backgroundColor
        2,                          // border
        "#cccccc",                  // borderColor
        5,                          // borderRadius
        "Arial",                    // labelFont
        "20px",                     // labelSize
        "#ffffff",                  // labelColor
        true,                       // labelshadow
        "%",                        // labelsuffix
        true,                       // gradient
        "none",                     // effect
        100                         // maxValue
    );

    // Das Ergebnis in den Ziel-Datenpunkt schreiben
    setState('0_userdata.0.meine_bar', barHTML);
});
</code></pre>
<p dir="auto"><strong>Hinweis zu anderen Visualisierungen:</strong><br />
Dieses Skript generiert sauberen HTML/CSS-Code. Dadurch sollte es nicht nur in der klassischen vis, sondern auch in anderen Visualisierungen innerhalb von ioBroker (wie vis-2, jarvis oder anderen Adaptern, die HTML-Widgets unterstützen) problemlos funktionieren.</p>
<p dir="auto"><strong>Solltest du das Skript in einem anderen Adapter einsetzen und Probleme bei der Darstellung haben, empfehle ich, kurz im jeweiligen Unterforum des Adapters nachzufragen – meist ist dort nur eine kleine Anpassung der CSS-Klassen oder des Container-Wrappers nötig.</strong></p>
<p dir="auto">Viel Erfolg beim Einbinden in dein Dashboard. Wer mag, kann sich auch die <a href="https://forum.iobroker.net/topic/84905/dynamische-svg-progress-bar">SVG-Version</a> ansehen.</p>
<p dir="auto">Ro75.</p>
<p dir="auto">EDIT: Version 1.0.1</p>
<ul>
<li><strong>maxValue</strong> kann nun definiert werden. <strong>Standard</strong> ist <strong>100</strong>.</li>
</ul>
]]></description><link>https://forum.iobroker.net/topic/84899/dynamische-html-progress-bar</link><generator>RSS for Node</generator><lastBuildDate>Wed, 08 Jul 2026 23:25:04 GMT</lastBuildDate><atom:link href="https://forum.iobroker.net/topic/84899.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 01 Jul 2026 11:35:52 GMT</pubDate><ttl>60</ttl></channel></rss>