FTUI Widget StackedBar

Aus FHEMWiki

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.

Beispiel:

  • 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 folgen.

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-max Zahlenwert, bei welchem der Balken zu 100% gefüllt ist 1 data-max="20.0"
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="m³"
data-interval Anzahl Millisekunden, nach denen das Widget aktualisiert werden soll 0 -> kein Auto-Refresh data-interval="5000"


Installation

Widget-Datei widget_bar.js

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

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

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

"use strict";

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

var Modul_bar = function () {
    
    function drawBar(elem) {
        
        var id = elem.prop('id');
        var max = elem.data('max');
        var val = elem.data('value');
        var uni = elem.data('unit');
        
        var val1 = Math.floor(val / max * 228 + 24);
        var style;
        var textpos;
        if (val1 > 148) {
            textpos = 28;
            style = "text-anchor:start;font-family:Helvetica;font-size:30px;font-weight:bold";
        } else {
            textpos = 252;
            style = "text-anchor:end;font-family:Helvetica;font-size:30px;font-weight:bold";
        }
        var bar = document.getElementById(id);
        if (bar) {
            bar.getElementsByClassName("side")[0].setAttribute("width", val1 + 16);
            bar.getElementsByClassName("top")[0].setAttribute("x", val1);
            bar.getElementsByClassName("topline")[0].setAttribute("x", val1);
            bar.getElementsByClassName("labele")[0].textContent = val + " " + uni;
            bar.getElementsByClassName("labele")[0].setAttribute("x", textpos);
            bar.getElementsByClassName("labele")[0].setAttribute("style", style);
        }
    }
    
    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('max', 100);
        elem.initData('font-size', 12);
        
        elem.initData('interval', 5000);
    }
    
    function init_ui(elem) {
        var svg = document.getElementById('svg200x150')
        var p = document.getElementById("barwidget");
        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 color = elem.data('color');
        switch (color) {
            case "red":
            bar.getElementsByClassName("side")[0].setAttribute("fill", "url(#grad1r)");
            bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2r)");
            break;
            case "green":
            bar.getElementsByClassName("side")[0].setAttribute("fill", "url(#grad1g)");
            bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2g)");
            break;
            case "blue":
            bar.getElementsByClassName("side")[0].setAttribute("fill", "url(#grad1b)");
            bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2b)");
            break;
            case "pink":
            bar.getElementsByClassName("side")[0].setAttribute("fill", "url(#grad1p)");
            bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2p)");
            break;
            case "orange":
            bar.getElementsByClassName("side")[0].setAttribute("fill", "url(#grad1o)");
            bar.getElementsByClassName("top")[0].setAttribute("fill", "url(#grad2o)");
            break;
        }
    }
    
    function update(dev, par) {
        
        me.elements.filterDeviceReading('get', dev, par).each(function (index) {
            var elem = $(this);
            var val = elem.getReading('get').val;
            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'));
            elem.data('value', val);
            drawBar(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: 'bar',
        init_attr: init_attr,
        init_ui: init_ui,
        update: update
    });
    
    return me;
};

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. 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:

    <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>
        <!-- ++++++++++++++++++++++++++++++++++++ Bar Widget ++++++++++++++++++++++++++++++++++++++ -->
        <g id="barwidget">
          <g transform="translate(0,65)">
            <rect x="24" y="5" width="40" height="80" rx="20" ry="40" fill="url(grad0)"/>
            <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"/>
            <rect x="24" y="5" width="268" height="80" rx="20" ry="40" fill="none"
              stroke="rgb(139, 137, 137)" stroke-width="2"/>
            <text class="labele" x="28" y="52" fill="rgb(75, 75, 75)"
              style="font-family:Helvetica;font-size:24px;font-weight:bold;"/>
          </g>
        </g>
       <!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
      </defs>
    </svg>