Moderne Weboberflächen möglichst effizient erstellen – am Beispiel vom Aquatimer
Im Juni dieses Jahres durften wir, der damalige Naturwissenschaft-Forder-Kurs aus Jahrgang elf, ein Projekt auf der Ideenexpo 2024 (Wettbewerb Ideenfang) vorstellen. Etwa ein halbes Jahr hatten wir Zeit ein vollautomatisches Bewässerungssystem für Zimmerpflanzen inklusive Steuerung vom Handy auf die Beine zu stellen. In diesem Artikel werde ich beschreiben, welche Technologien und Bibliotheken verwendet wurden, um in diesem Zeitraum möglichst schnell die Oberfläche dafür zum Laufen zu bekommen.
Wie und warum wir bei dem Projekt von einem ESP-32-Mikrocontroller auf einen Raspberry Pi gewechselt sind, kannst du im Artikel von Tobias nachlesen, in dem er genauer den Minicomputer, der das Ganze steuert, beleuchtet.
Mein Teil, das sogenannte Frontend, also die Website, mit der man das Ganze als Endbenutzer steuern kann, war von diesem Wechsel glücklicherweise unbetroffen. Dennoch stellte es sich recht schnell heraus, dass uns nicht mehr allzu viel Zeit blieb, was auch bei der Auswahl der Technologien zu berücksichtigen war.
Funktionen & Übersicht!
Das finale Produkt sah letztendlich (Ende der Messe) wie folgt aus:
Die wichtigsten Aspekte:
- Kopf- und Fußzeile: Infos wie aktueller Feuchtigkeitsgehalt, ausgewählter Bewässerungsmodus, Status der Pumpe und Verbindung
- linker Teil: Graph der Messwerte und eingezeichnetem Grenzwert (rote Linie)
- rechter Teil: Auswahl der Bewässerungsmodi und deren Einstellungen
Aber nun zu der Technik dahinter:
Haupt-Framework
Vielleicht hast du schonmal von HTML gehört. Damit wird „klassischerweise“ die Struktur einer Website beschrieben. Inhaltliche Aktualisierungen, wie zum Beispiel hier ein neuer Feuchtigkeitsgehalt, sind auch einigermaßen einfach per Javascript möglich.
Allerdings wird das mit der Zeit recht schnell unübersichtlich und ist sehr fehleranfällig – dafür hatten wir weder Zeit noch Nerven.
Abhilfe schaffen hier Javascript-Frameworks, die die Oberfläche automatisch synchron mit den sich ändernen Daten halten.
Da ich vorher schon mit Svelte gearbeitet habe, habe ich mich für dieses Framework entschieden. Dies ermöglicht es uns ebenfalls, mehrfach genutzte Teile unserer Weboberfläche in einzelne Komponenten zu abstrahieren und diese immer wiederzuverwenden.
Ein einfacher Zähler lässt sich sehr simpel in effektiv sechs Zeilen Code realisieren. Selbst wenn du noch nicht programmieren kannst, solltest du folgenden Code schon gut verstehen können:
Zum Vergleich hier der gleiche Zähler, ohne irgendein Framework (sog. „Vanilla-JS“) – Beispiel gekürzt:
Das Aussehen ist die halbe Miete
Natürlich hätten wir 100 Prozent unserer Zeit für die reine Funktionalität der Oberfläche verwenden können. Aber seien wir mal ehrlich: Für ein so großes Publikum wie das der Ideenexpo, sollte das Interface schon einigermaßen hübsch aussehen:
Bedeutet für uns: Eine Komponenten-Bibliothek (engl. „UI-Library“: gängiger Ausdruck) muss her! Denn die Kapazität ein eigenes Design-System und die dazugehörigen Knöpfe, Regler und Textfelder zu bauen hatten wir auf keinen Fall. Genau diese Bausteine bringt eine Komponenten-Bibliothek formschön mit.
Das Aquatimer Einstellungs-Fenster, welches ohne großen Aufwand doch sehr passabel aussieht.
Zugegeben, diese Auswahl ist rein subjektiv, denn diese Komponenten-Bibliotheken gibt es wie Sand am Meer. Ja, auch für unser Framework, Svelte. Im Prinzip geht dann wirklich nur noch um das Aussehen. Ich habe mich schlussendlich für shadcn-svelte entschieden, ein Port von einer sehr populären Bibliothek für ein anderes Framework (React; shadcn/ui für React).
Weitere Anpassungen via Tailwind-CSS
Ganz ohne eigene Design-Anpassungen geht es dann aber doch nicht. Insbesondere, wenn es um das generelle Layout und auch andere kleine Änderungen geht.
Und mehr „Rückenwind“ gibt es quasi gar nicht: Denn mithilfe von Tailwind-CSS müssen wir uns nämlich keine Klassen-Namen mehr ausdenken. Stattdessen stellt uns Tailwind eigene Utility-Klassen zur Verfügung, mit denen man die Style-Anpassungen direkt im Template schreibt.
Wenn wir z.B. einen großen, fett gedruckten, roten Text haben möchten sieht das wie folgt aus:
Normalerweise würden wir dies wie folgt schreiben:
Ähnlich wie beim vorherigen Beispiel von Svelte, sparen wir uns einige Zeilen.
Neben der Flexibilität, dass wir uns weder Klassennamen ausdenken noch verwalten müssen, bringt Tailwind auch eine Standard-Farbpalette und Größenwerte mit, die von Haus aus einfach gut zueinander passen. Auch das hat enorm Zeit gespart.
In Kombination mit „shadcn/ui“ konnten wir so relativ schnell das Design umsetzen.
Darstellung des Graphen
Der Grundstein wäre somit gelegt. Aber neben der Möglichkeit das Bewässerungsverhalten zu konfigurieren, ist die Anzeige der historischen Werte mit der wichtigste Teil der Oberfläche. Dieser wird komplett von der Bibliothek Chart.js verwaltet. Im Prinzip müssen wir so nur noch in einer Konfiguration beschreiben wie genau das Diagramm aussehen soll, also beispielsweise Achsenbeschriftungen, Farben & Co. Auch das Hinzufügen von neuen Messwerten ist damit ein Kinderspiel.
Echtzeit-Kommunikation mit dem Server
Selbstverständlich sollen Änderungen, wie neue Messwerte oder Einstellungen, in Echtzeit auf der Oberfläche erscheinen. Niemand möchte für so etwas die Seite neu laden müssen.
Technisch gesehen eignet sich dafür eine Websocket-Verbindung. Das Ganze kann man sich wie ein Telefongespräch zwischen Server und Browser vorstellen: Zunächst wird die Verbindung aufgebaut, anschließend können sowohl Server als auch Browser Nachrichten versenden.
Ein Beispiel: Der Browser teilt dem Server mit, dass der aktive Bewässerungsmodus geändert werden soll. War diese Aktualisierung erfolgreich, verschickt der Server an alle verbundenen Browser die Nachricht, mit dem neuen Bewässerungsmodus. Daraufhin wird dieser überall in der Oberfläche angezeigt.
Damit das funktioniert, muss natürlich das Austauschformat standardisiert sein. Und auch die verschiedenen Nachrichtentypen müssen auf beiden Seiten korrekt implementiert sein. Da wir dies vorher leider nicht geschafft haben, gingen alleine dafür in der ersten Nacht in Hannover einige Stunden Schlaf drauf.
Fazit
Schlussendlich mussten wir dann doch auf der Messe an sich den einen oder anderen Fehler beheben und Funktionen ausbauen. Für ausgiebige Tests hatten wir vorher eben nicht die nötige Zeit.
Hätten wir uns aber sowohl beim Server als auch bei der Oberfläche für andere, vielleicht „etabliertere“ Lösungen entschieden, so wäre das Projekt gar nicht erst so weit gekommen.