Posts Tagged ‘Dashboard’

Mittwoch, 7. März 2018

Folien meines Lightning Talks zum Thema „RPi Dashboard“ (Backup)

Da letzte Woche die Web-Site der bernischen Lightning Talks offline gegangen ist, habe ich mich entschieden, den Foliensatz meines Vortrages vom 17. März 2014 auf meinem Blog zu Archivzwecken aufzuschalten:

2014-03-14-dashboard-lightning-talk.pdf

Tags: , , ,
Labels: IT

Keine Kommentare | neuen Kommentar verfassen

Donnerstag, 22. August 2013

Midori auf Raspberry Pi von jQuery-Effekten entlasten

Mein Raspberry Pi, welcher ein 24″ Dashboard speist, hat sich in den letzten Tagen vermehrt aufgehängt. Dies mag mit einer Aktualisierung der Dashboard-Software zusammenhängen, insbesondere wohl mit Anpassungen am JavaScript-Code.

Ich setze jQuery als JavaScript-Framework ein und steure damit einige visuelle Gimmicks; beispielsweise schwarze Eselsohren an aktualisierten Tiles, welche innert 30 Sekunden nach der Aktualisierung verblassen und dann ganz verschwinden. Auch wird das Doppelpunkt der Zeitanzeige (HH:MM) so animiert, dass es jede zweite Sekunde ausgeblendet wird.

Dies scheint dazu geführt zu haben, dass die schwachbrüstige CPU des Raspberry Pis voll ausgelastet war. Der Browser Midori, welcher als Vollbild läuft, beanspruchte teilweise bis zu 50% CPU-Last und auch das X-Window-System wieso ähnlich hohe Werte auf. Die Load Average des Raspberry Pis war über 1.

Ich entschied mich deshalb, in den JavaScript-Code eine Weiche einzubauen, welche die grafischen Animationen für schwachbrünstige Browser/Systeme auf ein Minimum beschränkte.

Leider hatte die jQuery-eigenen Konfigurationseinstellung Kollateralschäden zur Folge, weshalb ich den Code selber optimieren musste:

jQuery.fx.off = true;

… funktionierte nicht zufriedenstellend.

Nachfolgend einige Konstrukte, die seit gestern Abend in der Produktion laufen:

Browser-Weiche

var browserIsPerformant = true;
if(navigator.userAgent.match(/(Midori)/)) {
	browserIsPerformant = false;
}

Delay statt FadeOuts

An den zwei Orten, wo ich schnelle (250ms – eine Viertelsekunde) und langsame (30000ms – 30 Sekunden) FadeOuts implementiert hatte, baute ich mit der Variable browserIsPerformant eine Weiche ein. jQuery kennt die .delay()-Funktion, mit welcher Aktionen für eine benutzerdefinierte Zeit (Millisekunden) hinausgezögert werden können. Damit konnte ich sicherstellen, dass die Effekte sowohl in der abgespeckten als auch in der Vollversion des Dashboards zur selben Zeit endeten.

Leider kann .delay() nicht auf .toggle(), .show() und .hide() angewendet werden.

Eselsohren

	if(browserIsPerformant) {
		$(obj).children('.updated').fadeToggle(30000);
	}
	else {
		$(obj).children('.updated').delay(30000).fadeOut(1);
	}

Indem ich im else-Abschnitt den Delay auf 30 Sekunden setzte und den fadeOut auf 1 Millisekunde, ergab sich unter Midori neu keine Performance-Einbusse mehr.

Sekundenanzeige (Doppelpunkte)

	if(browserIsPerformant) {
		$('.separator').fadeTo(250,clockSeparatorMap[opacity]);
	}
	else {
		$('.separator').delay(250).fadeTo(1,clockSeparatorMap[opacity]);
	}

Auch hier arbeite ich mit der .delay()-Funktion, setze sie hier aber „nur“ auf 250 Millisekunden. Anschliessen wird der FadeOut innert 1 Millisekunde gemacht.

Nachtrag

Obwohl Midori nach diesen Anpassungen vorerst stabil lief, fror der Browser nach ca. 10 Stunden erneut ein.

Ich entschied mich deshalb, statt Midori auf Chromium zu setzen:

# apt-get install chromium

… und den Google-Browser folgendermassen zu starten (/etc/xdg/lxsession/LXDE/autostart, via Raspberry PI kiosk mode with Chromium.):

@xset s off
@xset -dpms
@xset s noblank

@chromium --kiosk --incognito http://domain.tld/dash

Auch Chromium (Version 22) hat Probleme mit den opulenten jQuery-Animationen, weshalb ich den JavaScript-Code noch ein wenig anpassen musste:

var browserIsPerformant = true;
if(navigator.userAgent.match(/(Midori)/) || navigator.userAgent.match(/(armv6l)/)) {
	browserIsPerformant = false;
}

Chromium trägt aktuell auch die Prozessorplattform im User Agent-String, im Falle von Raspberry Pi ist das ARM.

Tags: , , , ,
Labels: Web

Keine Kommentare | neuen Kommentar verfassen

Samstag, 22. Juni 2013

Auf Twitter API 1.1 wechseln

Am 11. Juni wurden die in meinem Dashboard eingebundenen Twitter-Feeds plötzlich still. Der Grund: Twitter hat an diesem Tag API 1.0 deaktiviert und macht es seither Entwicklern und Script-Kiddies schwer, mit wenigen Zeilen Code auf öffentliche Twitter-Feeds zuzugreifen.

Wer vor dem selben Problem wie ich steht, sei hier eine kurze Anleitung präsentiert, um die Grundfunktionalität wieder online zu schalten:

Ich habe mich für die PHP-Klasse Codebird entschieden, da ich keine Lust hatte, OAuth-Requests und sonstigen Firlefanz auf eigene Faust zu programmieren.

Zuerst eröffnet man über den offiziellen Entwicklungsbereich mit dem persönlichen Twitter-Konto eine neue Twitter-Applikation. Wichtig ist, dass man sich den Consumer Key und das Consumer Secret merkt, denn diese beiden Variablen werden von der Klasse zur Kommunikation mit Twitter benötigt — authentifizieren sie doch die Applikation gegenüber dem Kurznachrichtendienst.

Nachdem man codebird.php sowie — ganz wichtig — cacert.pem in einem Unterverzeichnis des Web-Roots abgelegt hat, integriert man die Klasse in die bestehen Infrastrukutur:

        function __construct() {
            parent::__construct();
            
            require_once(dirname(__FILE__) . '/twitter/codebird.php');
            \Codebird\Codebird::setConsumerKey('AAAAAAAAAAAAAAAAAAAAA', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
            $this->cb = \Codebird\Codebird::getInstance();
        }

Als nächstes benötigt man einen sog. Bearer Token. Einmal generiert kann dieser für jeden weiteren Request an die Twitter API wiederverwendet werden — sofern man den Token nicht manuell deaktiviert. Alternativ kann man diesen Token auch bei jedem Aufruf des PHP-Scripts neu von Twitters Server abholen, was aber nicht einer guten Entwicklungspraxis (Cacheing!) entspricht.

Den Bearer Token erhält man in etwa folgendermassen:

$reply = $this->cb->oauth2_token();
$bearerToken = $reply->access_token;
\Codebird\Codebird::setBearerToken($bearerToken);

Jetzt also ist man wie mit API 1.0 in der Lage, eine simple Abfrage abzusetzen — im vorliegenden Fall rufe ich die letzten Tweets von John Gruber ab:

$this->cb->setReturnFormat(CODEBIRD_RETURNFORMAT_JSON);
$jsonData = $this->cb->statuses_userTimeline(array('screen_name' => 'daringfireball','count' => 25),true); // 'true' is needed for app only auth

Damit die Kompatibilität von bestehendem Code mit der neuen Klasse gewahrt wird, nenne ich JSON explizit als Antwortformat. Ganz wichtig ist, dass die Codebird-Methode als zweiten Paramenter true übergeben erhält — dies weist die Klasse an, die Anfrage im Kontext einer Applikation und nicht eines bestimmten Benutzers abzusetzen.

Tags: , , , , , ,
Labels: Web

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 22. Mai 2013

Konkurrenz-Probleme in PHP

In den letzten Wochen habe ich intensiv an meinem Raspberry Pi-Dashboard für unser Apartment geschraubt und gebastelt. Verschiedene Tiles werden mittels AJAX-Requests, welche über jQuery abgesetzt werden, mit Inhalten versorgt — und dies regelmässig alle paar Minuten.

Ein mysteriöser Bug machte mir in den letzten Tagen das Leben schwer: Es konnte vorkommen, dass zwei Tiles mit den Aktienkursen desselben Unternehmens gefüllt wurden, obwohl die Tile-IDs unterschiedliche ISINs trug.

Nach einigen Minuten Debugging schwante mir dann plötzlich, was das Problem war: Der auf dem Server lokal zwischengespeicherte HTML-Cache war zwar in Dateien mit der richtigen ISIN im Dateinamen abgelegt — der Inhalt war aber identisch. Konkret enthielt die HTML-Datei über das Unternehmen AAPL die Daten für das Unternehmen GOOG.

Wie sich heraustellte, hatte ich mein Script nicht dermassen intelligent programmiert, dass es mit mehreren, nur wenige Millisekunden auseinanderliegenden Requests ordnungsgemäss umging. Um nämlich den Aktienkurs einer ISIN abzufragen, muss dieser zuerst über einen HTTP-Request an ein Suchscript meines unfreiwilligen Kursanbieters in eine vom Anbieter intern verwendete NOTATION_ID umgewandelt werden (was wissbegierigen Lesern dieses Blogs einen Hinweis gibt, von welcher Web-Site ich die Kursdaten beziehe). Erst danach kann ich die Informationsseite des auf dem Aktienmarkt gehandelten Unternehmens aufrufen, herunterladen und im lokalen Dateisystem ablegen.

Konkret war mein Fehler, dass ich die Suchergebnisse zur Umwandlung der ISIN in die NOTATION_ID in eine Cache-Datei mit statischem Dateinamen ablegte. Da die Requests mit wenigen Millisekunden Versatz beim Server ankamen, war die Cache-Datei manchmal bereits von einem späteren Request überschrieben worden, als ich den HTML-Code endlich in PHP einlesen wollte (ich denke, dass wir hier von einigen wenigen hundert Millisekunden sprechen). Dies führte dazu, dass ich einer ISIN fälschlicherweise die NOTATION_ID des nachfolgenden Requests zuwies und diese plötzlich für zwei Kursanbieter galt. Da ich dieses Mapping aus Performance-Gründen ebenfalls serialisiert in einer Cache-Datei ablegte, wurden ab diesem Zeitpunkt zwei Tiles mit identischen Inhalten befüllt.

Die erste Lösung, dem Dateinamen einen sich ständig ändernden Wert einzupflegen, scheiterte grandios, weil ich time() verwendete. Da die Requests innerhalb der selben Sekunde auf dem Server eintreffen konnten, konnte die Cache-Datei im schlimmsten Fall weiterhin vom nachfolgenden Request überschrieben werden. Die zweite Lösung war deshalb, durch das Einfügen einer mittels rand(1000,9999) definierten Zufallszahl einen wahrlich zufälligen Wert in den Dateinamen einzuschleusen. Die Chancen, einen identischen Cache-Namen zu erwischen, ist hier 1:10000 — good enough, würde ich sagen.

Natürlich hätte ich auch einfach die ISIN in den Namen der Cache-Datei aufnehmen können. Hier hegte ich aber Zweifel, weil ich nicht wollte, dass bei einem Downloadfehler einfach unbemerkt die bestehenden, veralteten Daten eingelesen würden.

Tags: , , , , , , ,
Labels: Programmierung

1 Kommentar | neuen Kommentar verfassen

Sonntag, 12. Mai 2013

Wetter-Icons als Web-Font in ein Dashboard einbinden

Derzeit arbeite ich mit Hochdruck an einem Dashboard, welches ich in unserer Mietwohnung in Form eines TFT-Bildschirms beim Eingangsbereich platzieren möchte. Das Dashboard basiert softwaretechnisch auf HTML, CSS, JavaScript und PHP. Es integriert gecachte HTML-Dateien, JSON-Abfragen (MEZI, Twitter) und sogar einen iCalendar.

Ein Element (resp. „Tile“) des Dashboards wird die Wettervorhersage für den morgigen Tag sein. Um nicht nur eine qualitative Beschreibung des Wetters anzuzeigen, sondern auch ein Icon, habe ich mich natürlich vom derzeitigen (technologischen wie designtechnischen) Platzhirschen forecast.io inspirieren lassen. Dessen mit HTML5-Canvas animierten Icons waren dann aber doch Overkill für mich.

Stattdessen habe ich auf die Meteocons Web-Font von Alessio Atzeni zurückgegriffen. Die Web-Font ist in der Public Domain und lässt sich somit problemlos in ein HTML5/CSS3-Web-Projekt einbinden.

Leider sind die Icons meines Wissens nicht einem standardisierten Wettertyp zugeordnet, weshalb ich dieses Mapping mittels eines PHP-Arrays von Hand nachholen musste. Das Resultat lässt sich durchaus sehen:

eMeidi.local Dashboard Weather Forecast
image-5401

Tags: , , , ,
Labels: Web

3 Kommentare | neuen Kommentar verfassen