FTUI Widget StackedBar: Unterschied zwischen den Versionen

Aus FHEMWiki
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
 
Zeile 1: Zeile 1:
Das [[{{PAGENAME}}|StackedBar Widget]] ist ein Widget für [[FHEM Tablet UI V2]], mit dem ein Reading eines FHEM-Devices in Form eines horizontalen Balkens mit rundem Querschnitt angezeigt wird. Dabei muss dieses Reading aus einer durch Leerzeichen getrennten Liste von Werten bestehen. Der Balken wird dann komplett ausgefüllt, und zwar so, dass jedem der Werte eine Breite zugewiesen wird, die seinem prozentualen Anteil an der Summe der Werte entspricht.
Das [[{{PAGENAME}}|StackedBar Widget]] ist ein Widget für [[FHEM Tablet UI V2]], mit dem ein Reading eines FHEM-Devices in Form eines horizontalen Balkens mit rundem Querschnitt angezeigt wird. Dabei muss dieses Reading aus einer durch Leerzeichen getrennten Liste von Werten bestehen. Der Balken wird dann komplett ausgefüllt, und zwar so, dass jedem der Werte eine Breite zugewiesen wird, die seinem prozentualen Anteil an der Summe der Werte entspricht.


Beispiel:
Es ist nicht in den Standard-Widgets von FTUI2 enthalten, zur Benutzung bitte den Anweisungen unter [[#Installation|Installation]] folgen.
* Der Wert des Reading ''energy_summary_out'' des Devices ''PowerFlow'' sei 2.264 0.000 10.579 0.126


Es ist nicht in den Standard-Widgets von FTUI2 enthalten, zur Benutzung bitte den Anweisungen unter [[#Installation|Installation]] folgen.
==Beispiele==
[[File:StackedBar1.png|1000px]]


<gallery>
* Der Wert des Reading ''energy_summary_out'' des Devices ''PowerFlow'' sei 1.000 2.000 3.593 1.000
File:Widget_bar_1.png
* Mit dem nachfolgenden FTUI2-Code wird dann der horizontale Balken in vier Teile geteilt. Zu sehen ist ein türkisfarbener Abschnitt mit der Beschriftung ''Sp 1.0'', ein pinkfarbener Abschnitt mit der Beschriftung ''Wb 2.0''; ein grauer Abschnitt mit der Beschriftung ''Haus 3.6'' und ein orangefarbener Abschnitt mit der Beschriftung ''Up 1.0''.
File:Widget_bar_2.png
<pre>
File:Widget_bar_3.png
    <div id="energy_out" data-type="stackedbar"
</gallery>
      data-device="PowerFlow" data-get="energy_summary_out"
      data-caption='["Sp","Wb","Haus","Up"]' 
      data-fix="1" data-color='["turquois","pink","gray","orange"]'
      style="position:absolute;left:0px;top:0px;width:200px"></div>
</pre>
* Der Wert des Reading ''energy_summary_in'' des Devices ''PowerFlow'' sei 1.356 1.475 0.061
* Mit dem nachfolgenden FTUI2-Code wird dann der horizontale Balken in drei Teile geteilt. Zu sehen ist ein Abschnitt türkisfarbener Abscnhitt mit der Beschriftung ''Sp 1.4'', ein grüner Abschnitt mit der Beschriftung ''PV2 1.5'' und ein roter Abschnitt mit der Beschriftung ''Gr 0.1''.  
<pre>
    <div id="energy_in" data-type="stackedbar"
      data-device="PowerFlow" data-get="energy_summary_in"
      data-caption='["Sp","PV2","Gr"]' 
      data-fix="1" data-color='["turquois","green","red"]'
      style="position:absolute;left:0px;top:140px;width:200px"></div>
</pre>


==Attribute==
==Attribute==
Zeile 27: Zeile 40:
|'''data-fix'''||Angegebene Anzahl an Dezimalstellen einhalten||(-1 -> nicht numerisch)||
|'''data-fix'''||Angegebene Anzahl an Dezimalstellen einhalten||(-1 -> nicht numerisch)||
|-
|-
|'''data-max'''||Zahlenwert, bei welchem der Balken zu 100% gefüllt ist||1||data-max="20.0"
|'''data-caption'''||Array von Strings für die Beschriftung||1|| data-caption='["Sp","Wb","Haus","Up"]' 
|-
|'''data-color'''||Fester Wert für die Farbe des Balkens. Erlaubte Werte hängen von den Definitionen im SVG-Teil ab, siehe unten. Mit dem unten gegebenem Beispiel sind erlaubte Farben "red", "green", "bue", "pink", "orange" ||||data-color="pink"
|-
|-
|'''data-unit'''||Einheit nach Zahl hinzufügen||||data-unit=""
|'''data-color'''||Array von festen Werten für die Farbe der Abschnitte. Erlaubte Werte hängen von den Definitionen im SVG-Teil ab, siehe unten. Mit dem unten gegebenem Beispiel sind erlaubte Farben "red", "green", "blue", "turquois", "gray", "pink", "orange" ||||data-color='["turquois","pink","gray","orange"]'
|-
|-
|'''data-interval'''||Anzahl Millisekunden, nach denen das Widget aktualisiert werden soll||0 -> kein Auto-Refresh||data-interval="5000"
|'''data-interval'''||Anzahl Millisekunden, nach denen das Widget aktualisiert werden soll||0 -> kein Auto-Refresh||data-interval="5000"
Zeile 38: Zeile 49:


=Installation=
=Installation=
==Widget-Datei widget_bar.js==
==Widget-Datei widget_stackedbar.js==
Dazu muss im ersten Schritt eine Widget-Datei widget_­bar.­js erstellt werden, die (mit
Dazu muss im ersten Schritt eine Widget-Datei widget_­stackedbar.­js erstellt werden, die (mit
den korrekten Rechten) im Verzeichnis /opt/fhem/www/tablet/js gespeichert wird.
den korrekten Rechten) im Verzeichnis /opt/fhem/www/tablet/js gespeichert wird.
 
<div class="mw-collapsible mw-collapsed">
Code:<div class="mw-collapsible-content">
<pre>
<pre>
/* FTUI Plugin
/* FTUI Plugin
Zeile 52: Zeile 64:
"use strict";
"use strict";


function depends_bar() {
//
 
function depends_stackedbar() {
     var deps =[];
     var deps =[];
     return deps;
     return deps;
}
}


var Modul_bar = function () {
var Modul_stackedbar = function () {
      
      
     function drawBar(elem) {
     // privat sub function
    function drawStackedBar(elem) {
          
          
         var id = elem.prop('id');
         var id = elem.prop('id');
         var max = elem.data('max');
         var unit = elem.data('unit');
         var val = elem.data('value');
         var sum = elem.data('sum');
         var uni = elem.data('unit');
         var aval = elem.data('value');
       
         var acap = elem.data('caption');
         var val1 = Math.floor(val / max * 228 + 24);
         var num = aval.length;
         var style;
         var valx;
         var textpos;
         var valw;
         if (val1 > 148) {
        var bar = document.getElementById(id);
            textpos = 28;
         if (bar && (unit != '')) {
            style = "text-anchor:start;font-family:Helvetica;font-size:30px;font-weight:bold";
             bar.getElementsByClassName("labels")[0].textContent = sum + ' ' + unit;
         } else {
             textpos = 252;
            style = "text-anchor:end;font-family:Helvetica;font-size:30px;font-weight:bold";
         }
         }
         var bar = document.getElementById(id);
         var j = 0;
        if (bar) {
        for (var i = 0; i < num; i++) {
            bar.getElementsByClassName("side")[0].setAttribute("width", val1 + 16);
            if (i == 0) {
            bar.getElementsByClassName("top")[0].setAttribute("x", val1);
                valx = 24;
            bar.getElementsByClassName("topline")[0].setAttribute("x", val1);
                valw = Math.floor(228 * aval[i] / sum + 40);
            bar.getElementsByClassName("labele")[0].textContent = val + " " + uni;
            } else {
            bar.getElementsByClassName("labele")[0].setAttribute("x", textpos);
                valx = valx + (valw - 40);
            bar.getElementsByClassName("labele")[0].setAttribute("style", style);
                valw = Math.floor(228 * aval[i] / sum + 40)
            }
            var style;
            var textpos = valx + 10;
           
            if (bar) {
                bar.getElementsByClassName("side")[i].setAttribute("x", valx);
                bar.getElementsByClassName("side")[i].setAttribute("width", valw);
               
                if (aval[i] > 0) {
                    bar.getElementsByClassName("labele")[i].textContent = acap[i] + ' ' + aval[i];
                    bar.getElementsByClassName("labele")[i].setAttribute("x", textpos);
                    bar.getElementsByClassName("labele")[i].setAttribute("y", 40+15*j);
                    bar.getElementsByClassName("labele")[i].setAttribute("style", style);
                    j=1-j;
                } else {
                    bar.getElementsByClassName("labele")[i].textContent = '';
                }
            }
         }
         }
     }
     }
Zeile 94: Zeile 124:
         elem.initData('get', 'STATE');
         elem.initData('get', 'STATE');
         elem.initData('unit', '');
         elem.initData('unit', '');
       
         elem.initData('value', 20);
         elem.initData('value', 20);
        elem.initData('max', 100);
         elem.initData('font-size', 12);
         elem.initData('font-size', 12);
       
         elem.initData('interval', 5000);
         elem.initData('interval', 5000);
     }
     }
Zeile 104: Zeile 131:
     function init_ui(elem) {
     function init_ui(elem) {
         var svg = document.getElementById('svg200x150')
         var svg = document.getElementById('svg200x150')
         var p = document.getElementById("barwidget");
         var p = document.getElementById("stackedbarwidget");
         var svg_prime = svg.cloneNode(true);
         var svg_prime = svg.cloneNode(true);
         var p_prime = p.cloneNode(true);
         var p_prime = p.cloneNode(true);
Zeile 110: Zeile 137:
         var id = elem.prop('id');
         var id = elem.prop('id');
         document.getElementById(id).appendChild(svg_prime);
         document.getElementById(id).appendChild(svg_prime);
         var color = elem.data('color');
         var acolor = elem.data('color');
         switch (color) {
         for (var i = 0; i < acolor.length; i++) {
            case "red":
            var color = acolor[i];
            bar.getElementsByClassName("side")[0].setAttribute("fill", "url(#grad1r)");
            switch (color) {
            bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2r)");
                case "red":
            break;
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1r)");
            case "green":
                if (i == acolor.length -1) {
            bar.getElementsByClassName("side")[0].setAttribute("fill", "url(#grad1g)");
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2r)");
            bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2g)");
                }
            break;
                break;
            case "blue":
                case "green":
            bar.getElementsByClassName("side")[0].setAttribute("fill", "url(#grad1b)");
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1g)");
            bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2b)");
                if (i == acolor.length -1) {
            break;
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2g)");
            case "pink":
                }
            bar.getElementsByClassName("side")[0].setAttribute("fill", "url(#grad1p)");
                break;
            bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2p)");
                case "blue":
            break;
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1b)");
            case "orange":
                if (i == acolor.length -1) {
            bar.getElementsByClassName("side")[0].setAttribute("fill", "url(#grad1o)");
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2b)");
            bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2o)");
                }
            break;
                break;
                case "pink":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1p)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2p)");
                }
                break;
                case "orange":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1o)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2o)");
                }
                break;
                case "turquois":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1t)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2t)");
                }
                break;
                case "gray":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1f)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2f)");
                }
                break;
            }
         }
         }
     }
     }
      
      
     function update(dev, par) {
     function update(dev, par) {
       
         me.elements.filterDeviceReading('get', dev, par).each(function (index) {
         me.elements.filterDeviceReading('get', dev, par).each(function (index) {
             var elem = $(this);
             var elem = $(this);
             var val = elem.getReading('get').val;
             var sval = elem.getReading('get').val;
             val = ftui.getPart(val, elem.data('part'));
             if (sval) {
            val = me.substitution(val, elem.data('substitution'));
                var aval = sval.split(" ");
            val = me.map(elem.data('map-get'), val, val);
                var sum = 0;
            val = me.fix(val, elem.data('fix'));
                for (var i = 0; i < aval.length; i++) {
            elem.data('value', val);
                    var val = aval[i];
            drawBar(elem);
                    val = ftui.getPart(val, elem.data('part'));
                    val = me.substitution(val, elem.data('substitution'));
                    val = me.map(elem.data('map-get'), val, val);
                    val = me.fix(val, elem.data('fix'));
                    aval[i] = val;
                    sum += parseFloat(val);
                }
                elem.data('value', aval);
                elem.data('sum', sum);
                drawStackedBar(elem);
            }
         });
         });
     }
     }
Zeile 157: Zeile 218:
     var me = $.extend(parent, {
     var me = $.extend(parent, {
         //override or own public members
         //override or own public members
         widgetname: 'bar',
         widgetname: 'stackedbar',
         init_attr: init_attr,
         init_attr: init_attr,
         init_ui: init_ui,
         init_ui: init_ui,
Zeile 166: Zeile 227:
};
};
</pre>
</pre>
</div></div>
==Auf der FTUI-Seite==
==Auf der FTUI-Seite==
Die Seite, auf welcher das Bar-Widget angezeigt werden soll, benötigt einerseits eine unsichtbare Region, deren sichtbare Kopien später das Widget enthalten werden.  
Die Seite, auf welcher das StackedBar-Widget angezeigt werden soll, benötigt einerseits eine unsichtbare Region, deren sichtbare Kopien später das Widget enthalten werden.  
Dazu muss irgendwo auf dieser Seite eine HTML-Division eingebaut werden, gerne direkt nach dem <body>-Tag.
Dazu muss irgendwo auf dieser Seite eine HTML-Division eingebaut werden, gerne direkt nach dem <body>-Tag.
<pre>
<pre>
Zeile 177: Zeile 239:


Außerdem wollen wir natürlich die Farben des Widgets durch schicke Gradienten darstellen, und benötigen die grafische Struktur des Widgets. Dazu gibt es eine SVG-Region, die wegen einer Breite und Höhe von jeweils Null Pixel unsichtbar ist. Also gleich im Anschluss an die unsichtbare HTML-Division von oben:
Außerdem wollen wir natürlich die Farben des Widgets durch schicke Gradienten darstellen, und benötigen die grafische Struktur des Widgets. Dazu gibt es eine SVG-Region, die wegen einer Breite und Höhe von jeweils Null Pixel unsichtbar ist. Also gleich im Anschluss an die unsichtbare HTML-Division von oben:
<div class="mw-collapsible mw-collapsed">
Code:<div class="mw-collapsible-content">
<pre>
<pre>
     <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
Zeile 231: Zeile 295:
           <stop offset="100%" style="stop-color:rgb( 0, 205, 205);stop-opacity:1"/>
           <stop offset="100%" style="stop-color:rgb( 0, 205, 205);stop-opacity:1"/>
         </linearGradient>
         </linearGradient>
        <!-- ++++++++++++++++++++++++++++++++++++ Bar Widget ++++++++++++++++++++++++++++++++++++++ -->
  <!-- ++++++++++++++++++++++++++++++++++++ StackedBar Widget ++++++++++++++++++++++++++++++++++++++ -->
         <g id="barwidget">
         <g id="stackedbarwidget">
           <g transform="translate(0,65)">
           <g transform="translate(0,65)">
            <rect x="24" y="5" width="40" height="80" rx="20" ry="40" fill="url(grad0)"/>
             <rect class="bottomline" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"
            <rect x="252" y="5" width="40" height="80" rx="20" ry="40" fill="rgb(250,250,250)"/>
            <rect class="side" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="gray"/>
            <rect class="top" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="gray"/>
            <rect x="252" y="5" width="40" height="80" rx="20" ry="40" fill="none"
              stroke="rgb(139, 137, 137)" stroke-width="2"/>
             <rect class="topline" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"
               stroke="rgb(139, 137, 137)" stroke-width="2"/>
               stroke="rgb(139, 137, 137)" stroke-width="2"/>
             <rect x="24" y="5" width="268" height="80" rx="20" ry="40" fill="none"
             <rect id="side0" class="side" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"/>
            <rect id="side1" class="side" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"/>
            <rect id="side2" class="side" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"/>
            <rect id="side3" class="side" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"/>
            <rect class="top"    x="252" y="5" width="40" height="80" rx="20" ry="40" fill="gray"/>
            <rect class="topline" x="252" y="5" width="40" height="80" rx="20" ry="40" fill="none"
              stroke="rgb(139,137,137" stroke-width="2"/>
            <rect class="outerline" x="24" y="5" width="268" height="80" rx="20" ry="40" fill="none"
               stroke="rgb(139, 137, 137)" stroke-width="2"/>
               stroke="rgb(139, 137, 137)" stroke-width="2"/>
             <text class="labele" x="28" y="52" fill="rgb(75, 75, 75)"
            <text class="labels" x="28" y="0" fill="rgb(75, 75, 75)"
               style="font-family:Helvetica;font-size:24px;font-weight:bold;"/>
              style="font-family:Helvetica;font-size:35px;font-weight:bold;"/>
            <text class="labele" x="28" y="40" fill="rgb(75, 75, 75)"
              style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
            <text class="labele" x="28" y="65" fill="rgb(75, 75, 75)"
              style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
            <text class="labele" x="28" y="40" fill="rgb(75, 75, 75)"
              style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
             <text class="labele" x="28" y="65" fill="rgb(75, 75, 75)"
               style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
           </g>
           </g>
         </g>
         </g>
Zeile 252: Zeile 325:
     </svg>
     </svg>
</pre>
</pre>
</div></div>
[[Kategorie:FHEM Tablet UI V2|StackedBar]]
[[Kategorie:FHEM Tablet UI V2|StackedBar]]

Aktuelle Version vom 7. August 2024, 09:06 Uhr

Das StackedBar Widget ist ein Widget für FHEM Tablet UI V2, mit dem ein Reading eines FHEM-Devices in Form eines horizontalen Balkens mit rundem Querschnitt angezeigt wird. Dabei muss dieses Reading aus einer durch Leerzeichen getrennten Liste von Werten bestehen. Der Balken wird dann komplett ausgefüllt, und zwar so, dass jedem der Werte eine Breite zugewiesen wird, die seinem prozentualen Anteil an der Summe der Werte entspricht.

Es ist nicht in den Standard-Widgets von FTUI2 enthalten, zur Benutzung bitte den Anweisungen unter Installation folgen.

Beispiele

StackedBar1.png

  • Der Wert des Reading energy_summary_out des Devices PowerFlow sei 1.000 2.000 3.593 1.000
  • Mit dem nachfolgenden FTUI2-Code wird dann der horizontale Balken in vier Teile geteilt. Zu sehen ist ein türkisfarbener Abschnitt mit der Beschriftung Sp 1.0, ein pinkfarbener Abschnitt mit der Beschriftung Wb 2.0; ein grauer Abschnitt mit der Beschriftung Haus 3.6 und ein orangefarbener Abschnitt mit der Beschriftung Up 1.0.
    <div id="energy_out" data-type="stackedbar" 
      data-device="PowerFlow" data-get="energy_summary_out" 
      data-caption='["Sp","Wb","Haus","Up"]'  
      data-fix="1" data-color='["turquois","pink","gray","orange"]'
      style="position:absolute;left:0px;top:0px;width:200px"></div>
  • Der Wert des Reading energy_summary_in des Devices PowerFlow sei 1.356 1.475 0.061
  • Mit dem nachfolgenden FTUI2-Code wird dann der horizontale Balken in drei Teile geteilt. Zu sehen ist ein Abschnitt türkisfarbener Abscnhitt mit der Beschriftung Sp 1.4, ein grüner Abschnitt mit der Beschriftung PV2 1.5 und ein roter Abschnitt mit der Beschriftung Gr 0.1.
    <div id="energy_in" data-type="stackedbar" 
      data-device="PowerFlow" data-get="energy_summary_in" 
      data-caption='["Sp","PV2","Gr"]'  
      data-fix="1" data-color='["turquois","green","red"]'
      style="position:absolute;left:0px;top:140px;width:200px"></div>

Attribute

Attribut Beschreibung Standard-Wert Beispiel
data-device Name des Device, dessen Reading angezeigt werden soll data-get="G.Verb"
data-get Name des Readings, dessen Wert angezeigt werden soll data-get="power"
data-part RegEx oder Nummer des Wortes, nach welcher der angezeigte Text gefiltert werden soll
data-fix Angegebene Anzahl an Dezimalstellen einhalten (-1 -> nicht numerisch)
data-caption Array von Strings für die Beschriftung 1 data-caption='["Sp","Wb","Haus","Up"]'
data-color Array von festen Werten für die Farbe der Abschnitte. Erlaubte Werte hängen von den Definitionen im SVG-Teil ab, siehe unten. Mit dem unten gegebenem Beispiel sind erlaubte Farben "red", "green", "blue", "turquois", "gray", "pink", "orange" data-color='["turquois","pink","gray","orange"]'
data-interval Anzahl Millisekunden, nach denen das Widget aktualisiert werden soll 0 -> kein Auto-Refresh data-interval="5000"


Installation

Widget-Datei widget_stackedbar.js

Dazu muss im ersten Schritt eine Widget-Datei widget_­stackedbar.­js erstellt werden, die (mit den korrekten Rechten) im Verzeichnis /opt/fhem/www/tablet/js gespeichert wird.

Code:
/* FTUI Plugin
 * Copyright (c) 2018 Prof. Dr. Peter A. Henning
 * GPL License
 */

/* global ftui:true, Modul_widget:true */

"use strict";

//

function depends_stackedbar() {
    var deps =[];
    return deps;
}

var Modul_stackedbar = function () {
    
    // privat sub function
    function drawStackedBar(elem) {
        
        var id = elem.prop('id');
        var unit = elem.data('unit');
        var sum = elem.data('sum');
        var aval = elem.data('value');
        var acap = elem.data('caption');
        var num = aval.length;
        var valx;
        var valw;
        var bar = document.getElementById(id);
        if (bar && (unit != '')) {
            bar.getElementsByClassName("labels")[0].textContent = sum + ' ' + unit;
        }
        var j = 0;
        for (var i = 0; i < num; i++) {
            if (i == 0) {
                valx = 24;
                valw = Math.floor(228 * aval[i] / sum + 40);
            } else {
                valx = valx + (valw - 40);
                valw = Math.floor(228 * aval[i] / sum + 40)
            }
            var style;
            var textpos = valx + 10;
            
            if (bar) {
                bar.getElementsByClassName("side")[i].setAttribute("x", valx);
                bar.getElementsByClassName("side")[i].setAttribute("width", valw);
                
                if (aval[i] > 0) {
                    bar.getElementsByClassName("labele")[i].textContent = acap[i] + ' ' + aval[i];
                    bar.getElementsByClassName("labele")[i].setAttribute("x", textpos);
                    bar.getElementsByClassName("labele")[i].setAttribute("y", 40+15*j);
                    bar.getElementsByClassName("labele")[i].setAttribute("style", style);
                    j=1-j;
                } else {
                    bar.getElementsByClassName("labele")[i].textContent = '';
                }
            }
        }
    }
    
    function init_attr(elem) {
        
        //init standard attributes
        base.init_attr.call(me, elem);
        
        elem.initData('get', 'STATE');
        elem.initData('unit', '');
        elem.initData('value', 20);
        elem.initData('font-size', 12);
        elem.initData('interval', 5000);
    }
    
    function init_ui(elem) {
        var svg = document.getElementById('svg200x150')
        var p = document.getElementById("stackedbarwidget");
        var svg_prime = svg.cloneNode(true);
        var p_prime = p.cloneNode(true);
        var bar = svg_prime.appendChild(p_prime);
        var id = elem.prop('id');
        document.getElementById(id).appendChild(svg_prime);
        var acolor = elem.data('color');
        for (var i = 0; i < acolor.length; i++) {
            var color = acolor[i];
            switch (color) {
                case "red":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1r)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2r)");
                }
                break;
                case "green":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1g)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2g)");
                }
                break;
                case "blue":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1b)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2b)");
                }
                break;
                case "pink":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1p)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2p)");
                }
                break;
                case "orange":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1o)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2o)");
                }
                break;
                case "turquois":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1t)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2t)");
                }
                break;
                case "gray":
                bar.getElementsByClassName("side")[i].setAttribute("fill", "url(#grad1f)");
                if (i == acolor.length -1) {
                    bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2f)");
                }
                break;
            }
        }
    }
    
    function update(dev, par) {
        me.elements.filterDeviceReading('get', dev, par).each(function (index) {
            var elem = $(this);
            var sval = elem.getReading('get').val;
            if (sval) {
                var aval = sval.split(" ");
                var sum = 0;
                for (var i = 0; i < aval.length; i++) {
                    var val = aval[i];
                    val = ftui.getPart(val, elem.data('part'));
                    val = me.substitution(val, elem.data('substitution'));
                    val = me.map(elem.data('map-get'), val, val);
                    val = me.fix(val, elem.data('fix'));
                    aval[i] = val;
                    sum += parseFloat(val);
                }
                elem.data('value', aval);
                elem.data('sum', sum);
                drawStackedBar(elem);
            }
        });
    }
    
    // public
    // inherit all public members from base class
    var parent = new Modul_widget();
    var base = {
        init_attr: parent.init_attr
    };
    var me = $.extend(parent, {
        //override or own public members
        widgetname: 'stackedbar',
        init_attr: init_attr,
        init_ui: init_ui,
        update: update
    });
    
    return me;
};

Auf der FTUI-Seite

Die Seite, auf welcher das StackedBar-Widget angezeigt werden soll, benötigt einerseits eine unsichtbare Region, deren sichtbare Kopien später das Widget enthalten werden. Dazu muss irgendwo auf dieser Seite eine HTML-Division eingebaut werden, gerne direkt nach dem <body>-Tag.

  <div style="visibility:hidden">
      <svg id="svg200x150" xmlns="http://www.w3.org/2000/svg" 
           viewBox="0 0 300 225" width="200px" height="150px"> </svg>
    </div>

Außerdem wollen wir natürlich die Farben des Widgets durch schicke Gradienten darstellen, und benötigen die grafische Struktur des Widgets. Dazu gibt es eine SVG-Region, die wegen einer Breite und Höhe von jeweils Null Pixel unsichtbar ist. Also gleich im Anschluss an die unsichtbare HTML-Division von oben:

Code:
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
         viewBox="0 0 10 10" width="0px" height="0px">
      <defs>
        <!-- white-snowwhite -->
        <linearGradient id="grad0" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:white;stop-opacity:1"/>
          <stop offset="100%" style="stop-color:rgb(139, 137, 137);stop-opacity:1"/>
        </linearGradient>
        <!-- lightsalmon/red and lightsalmon/lightsalmon3 -->
        <linearGradient id="grad1r" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:rgb( 255, 192, 188);stop-opacity:1"/>
          <stop offset="100%" style="stop-color:red;stop-opacity:1"/>
        </linearGradient>
        <linearGradient id="grad2r" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:rgb( 255, 192, 188);stop-opacity:1"/>
          <stop offset="100%" style="stop-color:rgb( 255, 140, 105);stop-opacity:1"/>
        </linearGradient>
        <!-- LightGoldenrod1/DarkOrange and LightGoldenrod1/DarkGoldenrod3 -->
        <linearGradient id="grad1o" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:rgb( 255, 236, 139);stop-opacity:1"/>
          <stop offset="100%" style="stop-color:rgb( 205, 149, 12);stop-opacity:1"/>
        </linearGradient>
        <linearGradient id="grad2o" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:rgb( 255, 236, 139);stop-opacity:1"/>
          <stop offset="100%" style="stop-color:rgb( 205, 149, 12);stop-opacity:1"/>
        </linearGradient>
        <!-- pink/deeppink and pink/hotpink3 -->
        <linearGradient id="grad1p" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:rgb( 255, 192, 203);stop-opacity:1"/>
          <stop offset="100%" style="stop-color:rgb( 255, 20, 147);stop-opacity:1"/>
        </linearGradient>
        <linearGradient id="grad2p" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:rgb( 255, 192, 203);stop-opacity:1"/>
          <stop offset="100%" style="stop-color:rgb( 205, 96, 144);stop-opacity:1"/>
        </linearGradient>
        <!-- chartreuse/green and chartreuse/chartreuse3  -->
        <linearGradient id="grad1g" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:rgb( 127,255, 0);stop-opacity:1"/>
          <stop offset="100%" style="stop-color:green;stop-opacity:1"/>
        </linearGradient>
        <linearGradient id="grad2g" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:rgb( 127,255, 0);stop-opacity:1"/>
          <stop offset="100%" style="stop-color:rgb( 102, 205, 0);stop-opacity:1"/>
        </linearGradient>
        <!-- cyan/blue and cyan/cyan3 -->
        <linearGradient id="grad1b" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:cyan;stop-opacity:1"/>
          <stop offset="100%" style="stop-color:blue;stop-opacity:1"/>
        </linearGradient>
        <linearGradient id="grad2b" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" style="stop-color:cyan;stop-opacity:1"/>
          <stop offset="100%" style="stop-color:rgb( 0, 205, 205);stop-opacity:1"/>
        </linearGradient>
   <!-- ++++++++++++++++++++++++++++++++++++ StackedBar Widget ++++++++++++++++++++++++++++++++++++++ -->
        <g id="stackedbarwidget">
          <g transform="translate(0,65)">
            <rect class="bottomline" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"
              stroke="rgb(139, 137, 137)" stroke-width="2"/>
            <rect id="side0" class="side" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"/>
            <rect id="side1" class="side" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"/>
            <rect id="side2" class="side" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"/>
            <rect id="side3" class="side" x="24" y="5" width="40" height="80" rx="20" ry="40" fill="none"/>
            <rect class="top"     x="252" y="5" width="40" height="80" rx="20" ry="40" fill="gray"/>
            <rect class="topline" x="252" y="5" width="40" height="80" rx="20" ry="40" fill="none"
              stroke="rgb(139,137,137" stroke-width="2"/>
            <rect class="outerline" x="24" y="5" width="268" height="80" rx="20" ry="40" fill="none"
              stroke="rgb(139, 137, 137)" stroke-width="2"/>
            <text class="labels" x="28" y="0" fill="rgb(75, 75, 75)"
              style="font-family:Helvetica;font-size:35px;font-weight:bold;"/>
            <text class="labele" x="28" y="40" fill="rgb(75, 75, 75)"
              style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
            <text class="labele" x="28" y="65" fill="rgb(75, 75, 75)"
              style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
            <text class="labele" x="28" y="40" fill="rgb(75, 75, 75)"
              style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
            <text class="labele" x="28" y="65" fill="rgb(75, 75, 75)"
              style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
          </g>
        </g>
       <!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
      </defs>
    </svg>