FTUI eigene Widgets

Aus FHEMWiki

Eigene Widgets für FHEM Tablet UI zu erstellen ist relativ einfach. Dafür muss nur eine widget_mywidget.js im js-Folder neben den anderen Widgets angelegt werden.

Eine Minimalvariante einer widget_mywidget.js-Datei könnte so aussehen:

var Modul_mywidget = function () {

    function init() {
        alert('init called');
    }

    function update(device, reading) {
        alert('update called for' + device + ':' + reading );
    }

    var me = $.extend(new Modul_widget(), {
        widgetname: 'mywidget',
        init: init,
        update: update,
    });

    return me;
};

Am Ende der Datei steht die Definition der Public-Parameter. Neben dem Namen müssen mindestens die beiden Funktionen init() und update() implementiert sein.

init() wird einmal gleich nach dem Laden des Plugins aufgerufen, update() jedesmal, wenn Änderungen von Readings passieren.

Dieses FTUI-Plugin kann man jetzt so einbinden:

<div data-type="mywidget"></div>


Wenn das Plugin für ein UI-Control sein soll, das mehrfach auf der Seite vorkommt, kann man im init() per each() durch alle Elemente iterieren:

function init() {
     me.elements = $('[data-type="' + me.widgetname + '"]');
     me.elements.each(function (index) {
         alert('init #' + index + ' called');
    });
}


Wenn ein Plugin Daten abrufen soll, die NICHT in einem Reading von FHEM enthalten sind, empfiehlt es sich, eine Klasse zu definieren:

<div class="neueklasse" data-type="blabla" data-device='test-device' data-get='neueklasse'>}

Im JS kann man dann so diese Elemente auswählen bzw ihnen etwas zuweisen:

if (elem.is('.neueklasse')) {
   elem.html(text);	
}

Nicht vergessen: Wenn hier Daten aus Readings abgefragt werden sollen, müssen diese Readings in init() abonniert werden:

   elem.initData('reading123', 'testreading');
   me.addReading(elem, 'reading123');

Und dann in der Update-Routine muss auf diese Readings geprüft werden:

me.elements.filterDeviceReading('reading123', dev, par)
		  .add(me.elements.filterDeviceReading('testreading', dev, par))

Nur dann werden die Elemente bei Updates aus fhem auch aktualisiert.

Ausserdem empfiehlt es sich natürlich in fhem bei den betroffenen Devices ein

attr device event-on-change-reading .*

zu machen!

Updates werden aber nur gerufen, wenn man die entsprechenden Readings beim init() abonniert. Das passiert mit addReading(); (Funktion ist enthalten im Base-Widget: Modul_widget)

In diesem Beispiel fügen wir das Reading hinzu, das im Attribut data-get angegeben ist. Wir legen gleich noch als Default das Reading 'STATE' fest, für den Fall, dass data-get im HTML Code fehlt. Das passiert mit elem.initData();

function init() {

    me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
    me.elements.each(function (index) {

        var elem = $(this);  
        elem.initData('get', 'STATE');

        me.addReading(elem, 'get');
    });
}

Dann können wir unter update() auf die Notifizierungen warten. Wir filtern mit elements.filterDeviceReading() die Elemente heraus, die unter data-get das aktualisierte Reading haben und holen mit elem.getReading().val den aktuell bekannten Wert aus dem lokalen Cache.

elem.getReading().date würde den Zeitstempel des Readings liefern.

function update(device, reading) {
     me.elements.filterDeviceReading('get', device, reading)
         .each(function (index) {
             var elem = $(this);
             var value = elem.getReading('get').val;
             if (value) {
                 elem.html(value);
             }
         });
}

Zusammengefasst sieht unser FTUI Plugin jetzt so aus:

var Modul_mywidget = function () {

    function init() {

        me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
        me.elements.each(function (index) {

            var elem = $(this);  
            elem.initData('get', 'STATE');

            me.addReading(elem, 'get');
        });
    }

    function update(device, reading) {
        me.elements.filterDeviceReading('get', device, reading)
            .each(function (index) {
                var elem = $(this);
                var value = elem.getReading('get').val;
                if (value) {
                    elem.html(value);
                }
            });

    }

    var me = $.extend(new Modul_widget(), {
        widgetname: 'mywidget',
        init: init,
        update: update,
    });

    return me;
};

Im HTML würde man das jetzt so nutzen können.

<div data-type="mywidget" data-device='AvReceiver' data-get='volume'></div>