FTUI eigene Widgets - Beispiel: Unterschied zwischen den Versionen

Aus FHEMWiki
K (kategorisiert)
K (source tag(s) auf syntaxhighlight umgestellt)
 
(Eine dazwischenliegende Version von einem anderen Benutzer wird nicht angezeigt)
Zeile 21: Zeile 21:


aus:
aus:
<source lang="javascript">
<syntaxhighlight lang="javascript">
var Modul_mywidget = function () {
var Modul_mywidget = function () {
     widgetname: 'mywidget',
     widgetname: 'mywidget',
</source>
</syntaxhighlight>
wird:
wird:
<source lang="javascript">
<syntaxhighlight lang="javascript">
/* FTUI Plugin
/* FTUI Plugin
  * Copyright (c) 2018 by Bruchbude
  * Copyright (c) 2018 by Bruchbude
Zeile 33: Zeile 33:
var Modul_abfall = function () {
var Modul_abfall = function () {
     widgetname: 'abfall',
     widgetname: 'abfall',
</source>
</syntaxhighlight>


Wir erstellen einen Calender Eintrag für unsere Abfalldaten.
Wir erstellen einen Calender Eintrag für unsere Abfalldaten.
Dazu geben wir in der fhem Kommandozeile folgendes ein:
Dazu geben wir in der fhem Kommandozeile folgendes ein:
<source lang="bash">
<syntaxhighlight lang="bash">
define Abfallkalender Calendar ical file /opt/fhem/www/tablet/abfallkalender.ics
define Abfallkalender Calendar ical file /opt/fhem/www/tablet/abfallkalender.ics
</source>
</syntaxhighlight>
Die Datei wird von Hand runtergeladen (die diversen Städte unterscheiden sich hier, weshalb wir erstmal alles von Hand machen) und in das entsprechende Verzeichnis kopiert.
Die Datei wird von Hand runtergeladen (die diversen Städte unterscheiden sich hier, weshalb wir erstmal alles von Hand machen) und in das entsprechende Verzeichnis kopiert.


Nun auf der linux Kommandozeile die Dateirechte setzen:
Nun auf der linux Kommandozeile die Dateirechte setzen:
<source lang="bash">
<syntaxhighlight lang="bash">
sudo chown fhem:dialout /opt/fhem/www/tablet/abfallkalender.ics
sudo chown fhem:dialout /opt/fhem/www/tablet/abfallkalender.ics
</source>
</syntaxhighlight>
Später wird der download per knopfdruck erfolgen.
Später wird der download per knopfdruck erfolgen.




Nun erstellen wir einen Eintrag in unserer .html Datei
Nun erstellen wir einen Eintrag in unserer .html Datei
<source lang="HTML">
<syntaxhighlight lang="HTML">
<li data-col="6" data-row="1" data-sizex="1" data-sizey="3">
<li data-col="6" data-row="1" data-sizex="1" data-sizey="3">
<div class="cell" data-type="abfall" data-device="Abfallkalender" data-detail='["Grau","Blau","Gelb","Braun"]'></div>
<div class="cell" data-type="abfall" data-device="Abfallkalender" data-detail='["Grau","Blau","Gelb","Braun"]'></div>
</li>
</li>
</source>
</syntaxhighlight>


Als nächstes sehen wir im Abfallkalender-Modul nach welches Reading wir benötigen.
Als nächstes sehen wir im Abfallkalender-Modul nach welches Reading wir benötigen.


Die Daten befinden sich in '''modeUpcoming''' welches wir in unserem Modul in der init() funktion wie folgt eintragen:
Die Daten befinden sich in '''modeUpcoming''' welches wir in unserem Modul in der init() funktion wie folgt eintragen:
<source lang="javascript">
<syntaxhighlight lang="javascript">
elem.initData('get', 'STATE');
elem.initData('get', 'STATE');
me.addReading(elem, 'get');
me.addReading(elem, 'get');
</source>
</syntaxhighlight>
wird zu:
wird zu:
<source lang="javascript">
<syntaxhighlight lang="javascript">
elem.initData('modeUpcoming', 'modeUpcoming');
elem.initData('modeUpcoming', 'modeUpcoming');
me.addReading(elem, 'modeUpcoming'); // update() wird aufgerufen wenn modeUpcoming sich ändert
me.addReading(elem, 'modeUpcoming'); // update() wird aufgerufen wenn modeUpcoming sich ändert
</source>
</syntaxhighlight>


Dazu kommt eine Änderung in der update() Funktion:
Dazu kommt eine Änderung in der update() Funktion:
<source lang="javascript">
<syntaxhighlight lang="javascript">
me.elements.filterDeviceReading('get', device, reading)
me.elements.filterDeviceReading('get', device, reading)
var value = elem.getReading('get').val;
var value = elem.getReading('get').val;
</source>
</syntaxhighlight>
wird zu:
wird zu:
<source lang="javascript">
<syntaxhighlight lang="javascript">
me.elements.filterDeviceReading('modeUpcoming', device, reading)
me.elements.filterDeviceReading('modeUpcoming', device, reading)
var value = elem.getReading('modeUpcoming').val;
var value = elem.getReading('modeUpcoming').val;
</source>
</syntaxhighlight>


Dann bauen wir noch eine Ausgabe ein um zu sehen ob alles funktioniert.
Dann bauen wir noch eine Ausgabe ein um zu sehen ob alles funktioniert.
Damit sieht unser Modul so aus:
Damit sieht unser Modul so aus:


<source lang="javascript">
<syntaxhighlight lang="javascript">
/* FTUI Plugin
/* FTUI Plugin
  * Copyright (c) 2018 by Bruchbude
  * Copyright (c) 2018 by Bruchbude
Zeile 117: Zeile 117:
return me;
return me;
};
};
</source>
</syntaxhighlight>


Jetzt bauen wir das parsing für die calendar daten ein. Die Calendar Daten sehen so aus: <source lang="bash">20181211GraueTonnegdabfallkalender</source>  
Jetzt bauen wir das parsing für die calendar daten ein. Die Calendar Daten sehen so aus:  
<syntaxhighlight lang="bash">20181211GraueTonnegdabfallkalender</syntaxhighlight>  
...getrennt durch ein Semikolon. Das Datum nehmen wir für die Berechnung der Tage und die Farbe zur Unterscheidung der Tonnen.
...getrennt durch ein Semikolon. Das Datum nehmen wir für die Berechnung der Tage und die Farbe zur Unterscheidung der Tonnen.
Der Rest des Strings interessiert uns nicht.
Der Rest des Strings interessiert uns nicht.


<source lang="javascript">
<syntaxhighlight lang="javascript">
function update(device, reading) {
function update(device, reading) {
    me.elements.filterDeviceReading('modeUpcoming', device, reading).each(function (index){
    me.elements.filterDeviceReading('modeUpcoming', device, reading).each(function (index){
Zeile 140: Zeile 141:
    });
    });
}
}
</source>
</syntaxhighlight>
Die Funktion getDaysToEmpty() sieht so aus:
Die Funktion getDaysToEmpty() sieht so aus:
<source lang="javascript">
<syntaxhighlight lang="javascript">
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Get the number of days until garbage collection
// Get the number of days until garbage collection
Zeile 155: Zeile 156:
    return -1; // error
    return -1; // error
}
}
</source>
</syntaxhighlight>


Jetzt da wir wissen wie lange es noch dauert bis die Müllabfuhr kommt können wir ein wenig html bauen
Jetzt da wir wissen wie lange es noch dauert bis die Müllabfuhr kommt können wir ein wenig html bauen
<source lang="javascript">
<syntaxhighlight lang="javascript">
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Draw an icon
// Draw an icon
Zeile 170: Zeile 171:
return myHtml;
return myHtml;
}
}
</source>
</syntaxhighlight>


FTUI lädt die entsprechende .css für den benutzten font sobald dieser im html auftauchen, aber unser widget fügt den html text erst nachträglich zu der html Seite hinzu.  
FTUI lädt die entsprechende .css für den benutzten font sobald dieser im html auftauchen, aber unser widget fügt den html text erst nachträglich zu der html Seite hinzu.  
Zeile 176: Zeile 177:
Da wir uns natürlich nicht darauf verlassen können das immer ein font-awesome icon vor unserem widget steht tragen wir die css in unser widget ein um sicher zu gehen das alle icons auch zu sehen sind:
Da wir uns natürlich nicht darauf verlassen können das immer ein font-awesome icon vor unserem widget steht tragen wir die css in unser widget ein um sicher zu gehen das alle icons auch zu sehen sind:


<source lang="javascript">
<syntaxhighlight lang="javascript">
function depends_abfall() {
function depends_abfall() {
     $('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'lib/font-awesome.min.css" type="text/css" />');
     $('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'lib/font-awesome.min.css" type="text/css" />');
}
}
</source>
</syntaxhighlight>
damit sind unser vorläufiges widget wie folgt aus:
damit sind unser vorläufiges widget wie folgt aus:
<source lang="javascript">
<syntaxhighlight lang="javascript">
/* FTUI Plugin
/* FTUI Plugin
  * Copyright (c) 2018 by Bruchbude
  * Copyright (c) 2018 by Bruchbude
Zeile 254: Zeile 255:
return me;
return me;
};
};
</source>
</syntaxhighlight>


Jetzt kümmern wir uns um den data-detail Parameter womit wir die Tonnen auswählen können welche angezeigt werden.
Jetzt kümmern wir uns um den data-detail Parameter womit wir die Tonnen auswählen können welche angezeigt werden.
Zeile 261: Zeile 262:
Nicht jeder wird alle 4 Tonnen benötigen, also werden wir über einen Parameter die benötigten Tonnen selektieren können.
Nicht jeder wird alle 4 Tonnen benötigen, also werden wir über einen Parameter die benötigten Tonnen selektieren können.


[[Kategorie:FHEM_Tablet_UI]]
[[Kategorie:FHEM Tablet UI V2]]

Aktuelle Version vom 12. November 2024, 16:10 Uhr

Wir werden nun unser erstes eigenes FTUI Widget Schritt für Schritt erstellen. Es wird unterstellt das FTUI eigene Widgets bereits gelesen wurde.

Info blue.png
Wichtig: Nach jeder Änderung an einem widget muss der Browsercache geleert werden damit die Änderungen wirksam werden!


Als Grundgerüst dient FTUI eigene Widgets und daraus bauen wir nun unser neues Widget.

Name widget_abfall.js
Funktion Die Anzeige von Müllabfuhrdaten aus einer .ics Datei
Fhem-Modul Calendar

Als erstes benennen wir das widget um und fügen einen Copyright Vermerk hinzu.

aus:

var Modul_mywidget = function () {
    widgetname: 'mywidget',

wird:

/* FTUI Plugin
 * Copyright (c) 2018 by Bruchbude
 * Under MIT License (http://www.opensource.org/licenses/mit.license.php)
 */
var Modul_abfall = function () {
     widgetname: 'abfall',

Wir erstellen einen Calender Eintrag für unsere Abfalldaten. Dazu geben wir in der fhem Kommandozeile folgendes ein:

define Abfallkalender Calendar ical file /opt/fhem/www/tablet/abfallkalender.ics

Die Datei wird von Hand runtergeladen (die diversen Städte unterscheiden sich hier, weshalb wir erstmal alles von Hand machen) und in das entsprechende Verzeichnis kopiert.

Nun auf der linux Kommandozeile die Dateirechte setzen:

sudo chown fhem:dialout /opt/fhem/www/tablet/abfallkalender.ics

Später wird der download per knopfdruck erfolgen.


Nun erstellen wir einen Eintrag in unserer .html Datei

	<li data-col="6" data-row="1" data-sizex="1" data-sizey="3">
		<div class="cell" data-type="abfall" data-device="Abfallkalender" data-detail='["Grau","Blau","Gelb","Braun"]'></div>
	</li>

Als nächstes sehen wir im Abfallkalender-Modul nach welches Reading wir benötigen.

Die Daten befinden sich in modeUpcoming welches wir in unserem Modul in der init() funktion wie folgt eintragen:

	elem.initData('get', 'STATE');
	me.addReading(elem, 'get');

wird zu:

	elem.initData('modeUpcoming', 'modeUpcoming');
	me.addReading(elem, 'modeUpcoming'); // update() wird aufgerufen wenn modeUpcoming sich ändert

Dazu kommt eine Änderung in der update() Funktion:

	me.elements.filterDeviceReading('get', device, reading)
	var value = elem.getReading('get').val;

wird zu:

	me.elements.filterDeviceReading('modeUpcoming', device, reading)
	var value = elem.getReading('modeUpcoming').val;

Dann bauen wir noch eine Ausgabe ein um zu sehen ob alles funktioniert. Damit sieht unser Modul so aus:

/* FTUI Plugin
 * Copyright (c) 2018 by Bruchbude
 * Under MIT License (http://www.opensource.org/licenses/mit.license.php)
 */
var Modul_abfall = function () {
	function init() {
		me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
		me.elements.each(function (index) {
			var elem = $(this);
			elem.initData('modeUpcoming', 'modeUpcoming');
			me.addReading(elem, 'modeUpcoming');
		});
	}

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

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

Jetzt bauen wir das parsing für die calendar daten ein. Die Calendar Daten sehen so aus:

20181211GraueTonnegdabfallkalender

...getrennt durch ein Semikolon. Das Datum nehmen wir für die Berechnung der Tage und die Farbe zur Unterscheidung der Tonnen. Der Rest des Strings interessiert uns nicht.

	function update(device, reading) {
	    me.elements.filterDeviceReading('modeUpcoming', device, reading).each(function (index){
		var elem = $(this);
		var value = elem.getReading('modeUpcoming').val;
		if (!value) return; // error: no data found in calendar modul
		var strArray = value.split(";");
		strArray.sort(); // just to be sure all is sorted
		var today = new Date();
		today = parseInt(today.yyyymmdd().replace(/-/g,"")); // delete all "-" and convert to int
alert("grau: "+	getDaysToEmpty(strArray, "Grau", today)   );
		getDaysToEmpty(strArray, "Blau", today);
		getDaysToEmpty(strArray, "Gelb", today);
		getDaysToEmpty(strArray, "Braun", today);
		elem.html();
	    });
	}

Die Funktion getDaysToEmpty() sieht so aus:

	//////////////////////////////////////////////////////////////////////////////
	// Get the number of days until garbage collection
	//////////////////////////////////////////////////////////////////////////////
	function getDaysToEmpty(array, wasteColor, today){
	    var waste = array.filter(s => s.includes(wasteColor)); // get all dates for the selected waste container
	    for (var i =0; i< waste.length; ++i)
	    {
		if (parseInt(waste[i]) >= today) // we need nothing from the past
		    return parseInt(waste[i]) - today;
	    }
	    return -1; // error
	}

Jetzt da wir wissen wie lange es noch dauert bis die Müllabfuhr kommt können wir ein wenig html bauen

	//////////////////////////////////////////////////////////////////////////////
	// Draw an icon
	//////////////////////////////////////////////////////////////////////////////
	function addIcon(days, vertical, colorCircle, colorWaste)
	{
		var myHtml= "<div class='famultibutton fa-stack fa-2x' onclick='this.childNodes[1].classList.remove(\"blink\")'>"
		myHtml+= "<i id='bg' class='fa fa-stack-2x fa-circle-thin' style='color: "+colorCircle+";'></i>"
		myHtml+= "<i id='fg' class='fa fa-stack-1x fa-trash warn" + (days <2?' blink':'') + "' style='color: "+colorWaste+";'></i>"
		myHtml+= "<i id='warn'>"+ days +"</i></div>" + (vertical?"<br>":"");
		return myHtml;
	}

FTUI lädt die entsprechende .css für den benutzten font sobald dieser im html auftauchen, aber unser widget fügt den html text erst nachträglich zu der html Seite hinzu.

Da wir uns natürlich nicht darauf verlassen können das immer ein font-awesome icon vor unserem widget steht tragen wir die css in unser widget ein um sicher zu gehen das alle icons auch zu sehen sind:

function depends_abfall() {
     $('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'lib/font-awesome.min.css" type="text/css" />');
}

damit sind unser vorläufiges widget wie folgt aus:

/* FTUI Plugin
 * Copyright (c) 2018 by Bruchbude
 * Under MIT License (http://www.opensource.org/licenses/mit.license.php)
 */
var Modul_abfall = function () {
	function depends_abfall() {
		$('head').append('<link rel="stylesheet" href="' + ftui.config.basedir + 'lib/font-awesome.min.css" type="text/css" />');
	}

	function init() {
		me.elements = $('div[data-type="' + me.widgetname + '"]', me.area);
		me.elements.each(function (index) {
			var elem = $(this);
			elem.initData('modeUpcoming', 'modeUpcoming');
			me.addReading(elem, 'modeUpcoming');
		});
	}
	//////////////////////////////////////////////////////////////////////////////
	// Get the number of days until garbage collection
	//////////////////////////////////////////////////////////////////////////////
	function getDaysToEmpty(array, wasteColor, today){
	    var waste = array.filter(s => s.includes(wasteColor)); // get all dates for the selected waste container
	    for (var i =0; i< waste.length; ++i)
	    {
		if (parseInt(waste[i]) >= today) // we need nothing from the past
		    return parseInt(waste[i]) - today;
	    }
	    return -1; // error
	}

	//////////////////////////////////////////////////////////////////////////////
	// Draw an icon
	//////////////////////////////////////////////////////////////////////////////
	function addIcon(days, vertical, colorCircle, colorWaste)
	{
		var myHtml= "<div class='famultibutton fa-stack fa-2x' onclick='this.childNodes[1].classList.remove(\"blink\")'>"
		myHtml+= "<i id='bg' class='fa fa-stack-2x fa-circle-thin' style='color: "+colorCircle+";'></i>"
		myHtml+= "<i id='fg' class='fa fa-stack-1x fa-trash warn" + (days <2?' blink':'') + "' style='color: "+colorWaste+";'></i>"
		myHtml+= "<i id='warn'>"+ days +"</i></div>" + (vertical?"<br>":"");
		return myHtml;
	}

	//////////////////////////////////////////////////////////////////////////////
	// We need an update because Calendar data has changed
	//////////////////////////////////////////////////////////////////////////////
	function update(device, reading) {
	    me.elements.filterDeviceReading('modeUpcoming', device, reading).each(function (index){
		var elem = $(this);
		var value = elem.getReading('modeUpcoming').val;
		if (!value) return; // error: no data found in calendar modul
		var strArray = value.split(";");
		strArray.sort(); // just to be sure all is sorted
		var today = new Date();
		today = parseInt(today.yyyymmdd().replace(/-/g,"")); // delete all "-" and convert to int
		var vertical = true; //(höhe > breite?true:false);
		var myHtml="";
		myHtml+= addIcon(getDaysToEmpty(strArray, "Grau", today),vertical, "gray", "dimgray")
		myHtml+= addIcon(getDaysToEmpty(strArray, "Blau", today),vertical, "gray", "#4747ff");
		myHtml+= addIcon(getDaysToEmpty(strArray, "Gelb", today),vertical, "gray", "yellow");
		myHtml+= addIcon(getDaysToEmpty(strArray, "Braun", today),vertical,"gray", "saddlebrown");
		elem.html(myHtml);
	    });
	}

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

Jetzt kümmern wir uns um den data-detail Parameter womit wir die Tonnen auswählen können welche angezeigt werden. Gehen wir davon aus das in der Regel 4 Tonnen überwacht werden müssen: Restmüll/Plastik/Papier/Bio.

Nicht jeder wird alle 4 Tonnen benötigen, also werden wir über einen Parameter die benötigten Tonnen selektieren können.