FTUI Widget Bar

Aus FHEMWiki
Version vom 7. August 2024, 09:04 Uhr von Pahenning (Diskussion | Beiträge)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Das Bar 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.

Es ist nicht in den Standard-Widgets von FTUI2 enthalten, sondern wurde 2019 in [1] veröffentlicht. 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"
  1. P.A.Henning: Smart Home mit FHEM, Carl Hanser Verlag München 2019, 341 Seiten, ISBN 978-3-446-45873-4

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.

Code:
/* 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:

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>
        <!-- ++++++++++++++++++++++++++++++++++++ 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>