NEWS
Dynamische HTML-Progress-Bar
-
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.

Hinweis: Dieses Skript wurde mit Unterstützung von KI entwickelt, um eine effiziente und saubere Code-Struktur für die dynamische HTML-Generierung sicherzustellen.
Warum dieses Skript?
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".Einheitliches Design:
Alle Balken sind konsistent.Flexibel:
Einfache Anpassung von Farben, Größen und Stilen über Parameter.Performance:
Nutzt leichtgewichtiges Inline-HTML/CSS, daher sehr schnell in basic-html Widgets.Was ist nötig?
Du benötigst die Instanz javascript.Datenpunkt:
Erstelle einen Datenpunkt (Typ String) in 0_userdata.0, in dem das generierte HTML gespeichert wird.Widget:
Erstelle in deiner VIS ein basic-html Widget und binde den Datenpunkt per Bindings ein: {0_userdata.0.dein.datenpunkt}.Die Parameter-Erklärung
Parameter Typ Beschreibung orientation String "horizontal" oder "vertical" value Number Aktueller Wert in Prozent (0 - 100) progressColor String Farbe des Balkens (Hex, RGB oder RGBA) backgroundColor String Hintergrundfarbe (Hex, RGB, RGBA, "transparent") border Number Rahmenstärke in Pixeln borderColor String Rahmenfarbe (Hex, RGB oder RGBA) borderRadius Number Eckenabrundung in Pixeln labelFont String Schriftart (z.B. "Arial") labelSize String Schriftgröße (z.B. "20px") "0px" wenn das Label ausgeblendet werden soll labelColor String Textfarbe (Hex, RGB oder RGBA) labelshadow Boolean true für dezenten Text-Schatten labelsuffix String Anhang hinter dem Wert (z.B. "%") gradient Boolean true für automatischen Farbverlauf effect String "glanz" für Glas-Effekt oder "none" maxValue Number Standardwert = 100, kann aber auch jeder andere Wert sein Farbangaben & Transparenz
Die Funktion ist für Standard-CSS-Farben ausgelegt. Du kannst für alle Farb-Parameter (progressColor, backgroundColor, borderColor, labelColor) die folgenden Formate verwenden:Hex-Werte: (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.
RGB / RGBA: Du kannst Farben als rgb(r, g, b) oder mit Transparenz als rgba(r, g, b, alpha) definieren.
Beispiel: "rgba(0, 128, 0, 0.5)" für ein halbtransparentes Grün.
"transparent": Verwende das Schlüsselwort "transparent", um den Hintergrund komplett auszublenden.
Hier das Script:
// === 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 >> 16) + amt; let g = ((num >> 8) & 0xFF) + amt; let b = (num & 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 << 16) | (g << 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 = `<div style="position:absolute;left:0;top:0;width:100%;height:45%;background:rgba(255,255,255,0.35);"></div>`; } else { glanz = `<div style="position:absolute;left:0;top:0;width:45%;height:100%;background:rgba(255,255,255,0.35);"></div>`; } } let labelShadowStyle = ""; if (labelshadow === true) { labelShadowStyle = "text-shadow:1px 1px 2px black;"; } let finalLabelText = value + " " + labelsuffix; if (orientation === "horizontal") { return ` <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;"> <div style="width:${percent}%;height:100%;background:${finalProgressColor};position:relative;"> ${glanz} </div> <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;"> ${finalLabelText} </div> </div> `; } else { return ` <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;"> <div style="width:100%;height:${percent}%;background:${finalProgressColor};position:relative;"> ${glanz} </div> <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;"> ${finalLabelText} </div> </div> `; } }Anwendung mit on()-Trigger (Automatische Aktualisierung):
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:// 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); });Hinweis zu anderen Visualisierungen:
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.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.
Viel Erfolg beim Einbinden in dein Dashboard. Wer mag, kann sich auch die SVG-Version ansehen.
Ro75.
EDIT: Version 1.0.1
- maxValue kann nun definiert werden. Standard ist 100.
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