Aus Raspberry Pi Geek 03/2015

Mit Python, Flask und Bootstrap einen RasPi fernsteuern (Seite 2)

Listing 2

from flask import Flask
from flask import render_template
from flask_bootstrap import Bootstrap
app = Flask(__name__)
Bootstrap(app)
@app.route('/')
def hello_world():
    return 'Hello World!'
@app.route('/hello.html')
def hello():
    nachricht = "Hallo Welt!"
    return render_template('hello.html', nachricht=nachricht)
if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8080, debug=True)

In den Zeilen 1 und 3 integriert das Skript neben Flask noch Bootstrap. Bei @app.route('/') handelt es sich um einen Decorator, der eine Python-Funktion an eine URL bindet. In diesem Fall heißt die Funktion hello_world() und die URL ist das Wurzelverzeichnis / der App.

Der Decorator in Zeile 12 entwirft eine neue Route. Öffnen Sie die Datei http://IP-Adresse des RasPi:8080/hello.html im Browser, erkennt Flask das und ruft die Funktion hello() auf, deren Ausgabe render_template() an den Browser schickt.

In der Datei test.py weist der Code der Variable nachricht den Text Hallo Welt! zu, render_template() reicht den Inhalt der Variablen an hello.html (Listing 3) weiter. Im Browser erscheint ein schlichtes Hallo Welt! (Abbildung 3).

Listing 3

<!DOCTYPE html>
{% extends "bootstrap/base.html" %}
{% import "navbar.html" as nav %}
{% block navbar %}
    {{ nav }}
{% endblock navbar %}
{% block content %}
    {{ nachricht }}
{% endblock %}
Abbildung 3: Die Leiste mit den Schaltflächen zur Navigation aus der Datei »navbar.html« binden Sie bei Bedarf in alle Seiten ein.

Abbildung 3: Die Leiste mit den Schaltflächen zur Navigation aus der Datei »navbar.html« binden Sie bei Bedarf in alle Seiten ein.

Die HTML-Datei hello.html bindet in Zeile 2 zunächst das vorinstallierte Bootstrap-Template ein, dann folgen eine Navigationsleiste und ein Inhaltsblock, der von Zeile 9 bis 11 reicht. In Zeile 10 steht die Variable nachricht, die Flask nun durch den in test.py zugewiesenen Wert ersetzt.

Die Navigationsleiste aus Abbildung 3 findet Flask in der Datei templates/navbar.html und legt sie in der Variablen nav ab. Die Datei navbar.html legen Sie selbst an. Das Vorbild dafür stammt aus dem Bootstrap-Projekt [15]. Liegt sie im Verzeichnis templates, binden die anderen Dateien sie ebenfalls nach demselben Schema ein.

Im Netzwerk erreichen Sie die Webseite nur, weil der Entwickler in Zeile 18 von Listing 2 die 0.0.0.0 anstelle der üblichen 127.0.0.1 verwendet. Würde er sich für die zweite Option entscheiden, liefe das Beispiel nur auf dem Raspberry Pi selbst – eine Sicherheitsmaßnahme, um böswillige Entwickler fernzuhalten. Allerdings steht sie den gutwilligen im Wege, wenn die Flask-App in einer virtuellen Maschine läuft.

Die Option debug=True aktiviert den Debugger, dank ihr bemerkt Flask aber Änderungen an der Codebasis und lädt die veränderten Seiten automatisch neu – falls das Framework nicht wegen eines Fehlers im Code abstürzt.

Hoch die Flaschen!

Mit Flask sind noch wesentlich komplexere Setups möglich, wie der Blick in die Dokumentation [16] beweist, aber es geht eben auch schlicht. Abbildung 4 zeigt Auszüge der kompletten Anwendung [17]. Die Datei piserver.py ersetzt test.py und im Ordner img warten verschiedene Bilder. Letztlich verwendet die App jedoch nur vier Templates: Neben der bereits erwähnten navbar.html gibt es die Hauptseite main.html, die erscheint, wenn Sie auf den Raspberry-Pi-Schriftzug aus Abbildung 1 klicken. Sie enthält auch die Navigationsleiste und beliebigen Text.

Abbildung 4: Die App, die den RasPi steuert, besteht vordergründig nur aus wenigen Dateien.

Abbildung 4: Die App, die den RasPi steuert, besteht vordergründig nur aus wenigen Dateien.

In den Dateien stats.html (Abbildung 1) und dienste.html (Abbildung 2) steckt jeweils eine Tabelle. Erstere zeigt Daten an, die piserve.py über den Raspberry Pi erhebt, Letztere liefert HTML-Formulare aus, über die Sie Dienste auf dem RasPi starten und stoppen. Einen Preis für das Design gewinnt das Interface nicht, demonstriert aber einige der Template-Optionen.

Listing 4

@APP.route('/')
@APP.route('/main.html')
def mainhtml():
    return render_template('main.html')
@APP.route('/stats.html')
def stats():
    today = datetime.date.today()
    system = platform.system()
    node = platform.node()
    arch = platform.machine()
    user = os.getlogin()
    space = os.statvfs('/home/'+user)
    freespace = (space.f_frsize * space.f_bavail)/1024/1024
    get_uptime = subprocess.Popen('uptime', stdout=subprocess.PIPE)
    uptime = get_uptime.stdout.read()
    return render_template('stats.html', today=today, system=
                           system, node=node, arch=arch, user=user,
                           freespace=freespace, uptime=uptime)
@APP.route('/dienste.html')
def dienst():
    user = os.getlogin()
    node = platform.node()
    vnconline = None
    if os.path.exists('/home/'+user+'/.vnc/'+node+':1.pid'):
        vnconline = True
    try:
        myip = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        myip.connect(('8.8.8.8', 80))
        getip = myip.getsockname()[0]
        myip.close()
    except StandardError:
        getip = "IP nicht erkannt"
    return render_template('dienste.html', vnconline=vnconline,
                           getip=getip)
@APP.route('/<vncstatus>', methods=['POST'])
def vncsteer(vncstatus):
    if vncstatus == "startserver":
        try:
            server_up = ["tightvncserver", ":1", "-geometry",
                         "1024x768", "-depth", "24"]
            subprocess.call(server_up)
        except StandardError:
            print "Server startet nicht oder läuft bereits."
    if vncstatus == "stoppserver":
        try:
            server_down = ["tightvncserver", "-kill", ":1"]
            subprocess.call(server_down)
        except StandardError:
            print "Server läuft nicht oder lässt sich nicht beenden."
    return redirect('/dienste.html')
@APP.route('/reboot', methods=['POST'])
def reboot():
    passwd = request.form['password']
    rbt1 = subprocess.Popen(["echo", passwd], stdout=subprocess.PIPE)
    rbt2 = subprocess.Popen(["sudo", "-S", "reboot"], stdin=rbt1.
                            stdout, stdout=subprocess.PIPE)
    print rbt2.communicate()[0]
    return redirect('/dienste.html')
if __name__ == "__main__":
    APP.run(host='0.0.0.0', port=8080, debug=True)

Flaschenpost

Die Datei piserve.py besteht grob aus fünf Blöcken, die jeweils mit @APP beginnen. Der erste in Zeile 1 ruft lediglich die Startseite auf. Die stats()-Funktion ab Zeile 7 erhebt Daten zum Raspberry Pi, wobei die ersten fünf Zeilen denen aus Abbildung 1 gleichen. Um den freien Platz im Home-Verzeichnis zu messen, muss Python dann wissen, wo dieses ist (Zeile 15) und ein wenig rechnen (Zeile 16).

Möchten Sie einen Bash-Befehl an den RasPi senden und die Ausgabe wieder einlesen, greifen Sie am besten zur Klasse subprocess.Popen, die ein uptime-Kommando absetzt und sich die Ausgabe über stdout=subprocess.PIPE merkt (Zeile 18). Die landet in der nächsten Zeile in der Variablen uptime, bevor render_template() alle Variablen an stats() retourniert.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: HeftseitenPreis €0,99
(inkl. 19% MwSt.)
RASPBERRY PI GEEK KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Raspberry Pi Geek bei Google Play Readly Logo
Nach oben