FTUI3 Widget StackedBar

Aus FHEMWiki

Auf dieser Seite wird erläutert, wie man in FTUI3 die grafische Anzeige eines Reading eines FHEM-Devices in Form eines horizontalen Balkens mit rundem Querschnitt erstellen kann. 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.

StackedBar1.png

Diese Widgets wurden zuerst für FHEM Tablet UI V2 erstellt, siehe FTUI Widget StackedBar.

Installation

Zunächst muss eine Grafikdatei im Format SVG (Scalable Vector Graphics) erstellt werden, hier mit dem Namen widget_stackedbar.svg und dem Inhalt

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
    viewBox="0 0 300 200" width="200px" height="150px">
    <!-- ++++++++++++++++++++++++++++++++++++ StackedBar Widget ++++++++++++++++++++++++++++++++++++++ -->
    <g id="stackedbar{{suffix}}">
        <g transform="translate(0,0)">
            <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{{suffix}}" class="side" x="24" y="5" width="40" height="80" rx="20"
                ry="40" fill="{{fillside0}}"/>
            <rect id="side1{{suffix}}" class="side" x="24" y="5" width="40" height="80" rx="20"
                ry="40" fill="{{fillside1}}"/>
            <rect id="side2{{suffix}}" class="side" x="24" y="5" width="40" height="80" rx="20"
                ry="40" fill="{{fillside2}}"/>
            <rect id="side3{{suffix}}" class="side" x="24" y="5" width="40" height="80" rx="20"
                ry="40" fill="{{fillside3}}"/>
            <rect class="top" x="252" y="5" width="40" height="80" rx="20" ry="40"
                fill="{{filltop}}"/>
            <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 id="labels{{suffix}}" x="28" y="0" fill="rgb(75, 75, 75)"
                style="font-family:Helvetica;font-size:35px;font-weight:bold;"/>
            <text id="label0{{suffix}}" x="28" y="40" fill="rgb(75, 75, 75)"
                style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
            <text id="label1{{suffix}}" x="28" y="65" fill="rgb(75, 75, 75)"
                style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
            <text id="label2{{suffix}}" x="28" y="40" fill="rgb(75, 75, 75)"
                style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
            <text id="label3{{suffix}}" x="28" y="65" fill="rgb(75, 75, 75)"
                style="font-family:Helvetica;font-size:14px;font-weight:bold;"/>
        </g>
    </g>
</svg>

Darin sind die Parameter suffix, fillside0..3 und filltop enthalten. Diese Parameter werden gesetzt, indem die SVG-Datei an beliebiger Stelle und beliebig oft in eine FTUI3-Seite eingebunden wird, z.B. mit

<ftui-content suffix="<SUFFIX>" file="widget_stackedbar.svg" fillside0="<FARBE0>" fillside1="<FARBE1>" fillside2="<FARBE3>" fillside3="<FARBE3>" filltop="<FARBE4>"> 
  </ftui-content>
  • Der String SUFFIX wird durch FTUI3 in die aufgerufene Datei eingesetzt. Wichtig: Für jeden anzuzeigenden Balken muss es einen eigenen (und eindeutigen) suffix geben, da dieser verwendet wird, um die Identifier der SVG-Elemente zu setzen.
  • FARBE0 ... FARBE4 sind SVG-Farbspezifikationen, z.B. können diese sich auf vordefinierte Gradienten beziehen.

Desweiteren muss im Header der FTUI-Seite etwas JavaScript-Code eingebunden werden.

<script>       
const ftui_updatestackedbar = (captions,unit,suffix) => input => updatestackedbar(input,captions,unit,suffix);
    ...
function updatestackedbar(values, captions, unit, suffix) {;
    if (! values) return 'updatestackedbar has empty values argument'; 
    if (! captions) return 'updatestackedbar has empty captions argument'; 
    
    var aval = values.split(' ');
    var acap = captions.split(' ');
    var num = aval.length;
    var sum = 0.0;
    for (var i = 0; i < num; i++) {
      sum+= Number(aval[i]);
    }
    if (unit != '') document.getElementById("labels" + suffix).textContent = roundTo(sum,1) + ' ' + unit;     
    var j = 0;
    var valx;
    var valw;
    for (var i = 0; i < num; i++) {
        if (i == 0) {
            valx = 24;
            valw = Math.floor(228 * Number(aval[i]) / sum + 40);
        } else {
            valx = valx + (valw - 40);
            valw = Math.floor(228 * Number(aval[i]) / sum + 40)
        }
        var textpos = valx + 10;
        var side = document.getElementById("side" + i + suffix);
        if( ! side ) {
          alert( "side" + i + suffix + "not found");  
        }else{
          side.setAttribute("x", valx);
          side.setAttribute("width", valw);
        }
        var label =  document.getElementById("label" + i + suffix);
        if( ! label ) {
          alert( "label" + i + suffix + "not found");  
        }else{
        if (Number(aval[i]) > 0) {
           label.textContent = acap[i] + ' ' + roundTo(Number(aval[i]),1);
           label.setAttribute("x", textpos);
           label.setAttribute("y", 40 + 15 * j);
            j = 1 - j;
        } else {
            label.textContent = '';
        }
      }
    }
}

Schließlich muss noch an beliebiger Stelle auf der FTUI3-Seite dafür gesorgt werden, dass das Diagramm auch mit Werten aus FHEM befüllt wird. Das geschieht am Einfachsten durch ein <ftui-label>-Element

 <ftui-label [text]="DEVICE:READING | ftui_updatestackedbar('<LISTE DER LABELS>','<EINHEIT>','<SUFFIX>')" class="hidden">
        </ftui-label>
  • Das Reading <READING> sollte eine durch Leerzeichen separierte Liste von Zahlen enthalten (2,3, oder 4 Zahlen)
  • Der String <LISTE DER LABELS> sollte eine durch Leerzeichen separierte Liste aus derselben Anzahl von Bezeichnungen enthalten, die in das Diagramm geschrieben werden
  • Der String <EINHEIT> gibt eine Einheit an. Ist dieser Parameter nicht gleich einem leeren String, wird über dem Diagramm die Summe der übergebenen Zahlen zusammen mit der Einheit dargestellt. Ist er gleich einem leeren String, wird diese Summe nicht dargestellt.
  • Der String <SUFFIX> muss denselben Wert haben, wie bei dem eingebundenen Content-File

Anwendungsbeispiel

Electricity 2025.png

Auf einer FTUI-Seite werden Anzeigen für den Stromverbrauch und die Stromquellen benötigt. Der Stromverbrauch teilt sich auf in

  • Aufladen des Speichers = Sp
  • Wallbox = Wb
  • Direktverbrauch im Haus = Haus
  • Upload = Up.

Die Daten stehen im Reading energy_summary_out des FHEM-Device powerflowProfileC An diesem Tag sind ausnahmsweise zwei der Werte Null, ferner ist der Wert für die hochgeladene Energie negativ.

0.0 0.0 13.6 -0.1

Der Strombezug teilt sich auf in

  • Entladen des Speichers = Sp
  • Photovoltaik = PV2
  • Bezug aus dem Grid = Gr

Die Daten stehen im Reading energy_summary_in des FHEM-Device powerflowProfileC Die Summe dieser Werte beträgt 14.5 kWh

1.4 8.2 4.9 

Die Differenz zum Stromverbrauch ist ein systembedingter Rundngsfehler.

 <!-- battery,wallbox,house,upload -->
      <div style="position:absolute;left:0px;top:30px;width:200px;height:150px">
        <ftui-content file="widget_stackedbar.svg" suffix="out" fillside0="url(#grad1t)" fillside1="url(#grad1p)" fillside2="url(#grad1f)" fillside3="url(#grad1o)" filltop="url(#grad2o)"> 
        </ftui-content>
      </div>
      <div style="position:absolute;left:30px;top:95px">
        <ftui-label text="Stromverbrauch" size="14px" style="font-family:Helvetica;font-weight:bold;"></ftui-label>
      </div>
      <!-- total -->
      <div style="position:absolute;left:30px;top:130px;color:blue">
        <ftui-label [text]="powerflowProfileC:energy_consumed | fix(1)" size="35px" style="font-family:Helvetica;font-weight:bold;">
          <span slot="unit" class="size--2">kWh</span>
        </ftui-label>
        <ftui-label [text]="powerflowProfileC:energy_summary_out | ftui_updatestackedbar('Sp Wb Haus Up','','out')">
        </ftui-label>
        <ftui-label [text]="powerflowProfileC:energy_summary_in | ftui_updatestackedbar('Sp PV2 Gr','','in')">
        </ftui-label></div>
      <!-- battery, PV2, grid -->
      <div style="position:absolute;left:0px;top:160px;width:200px;height:150px">
        <ftui-content file="widget_stackedbar.svg" suffix="in" fillside0="url(#grad1t)" fillside1="url(#grad1g)" fillside2="url(#grad1r)" fillside3="none" filltop="url(#grad2r)"> 
        </ftui-content>
      </div>
      <div style="position:absolute;left:30px;top:225px">
        <ftui-label text="Stromquellen" size="14px" style="font-family:Helvetica;font-weight:bold;"></ftui-label>
      </div>