Windhunde – Windpark-Monitoring-App für iOS und Android
Live-Werte, Alarme und Anlagenprotokoll aller Windkraftanlagen auf dem Smartphone: eine native App mit React Native und Expo, die exakt dieselben Werte zeigt wie das Web-Dashboard – mit Zwei-Faktor-Anmeldung, Face ID und abgesicherter Fernsteuerung, self-hosted in Deutschland.
- Kunde
- Windhunde GmbH
- Branche
- Windenergie
- Leistungen
- App Development, Backend & API, Datenbank & Zeitreihen, Hosting & Betrieb
- Jahr
- 2026
Wer Windkraftanlagen betreibt, will jederzeit wissen, ob sie laufen, wie viel sie gerade leisten und ob irgendwo eine Störung ansteht. Genau dafür haben wir für die Windhunde GmbH eine Windpark-Monitoring-App gebaut: eine native App für iPhone, iPad und Android, die Live-Werte, Alarme, Anlagenprotokoll und Anlagenregister aufs Smartphone bringt. Die App ist das mobile Gegenstück zu einem Monitoring-Dashboard im Browser, das wir vorher für dieselben Anlagen entwickelt haben. Beide greifen auf dieselbe Schnittstelle und dieselbe Datenbank zu. Entwickelt hat beides unsere Agentur The Freelancer Marketing.
In dieser Case Study zeigen wir dir, wie eine App zur Überwachung von Windkraftanlagen entsteht, wenn die Anlagen selbst schon viele Jahre alt sind und ihre Daten nur über eine kleine Steuerbox im Turm herausgeben. Wir erklären die Ausgangslage im Windpark, die Datenquelle, die Live-Übertragung, den Aufbau der App, die Sicherheitsarchitektur rund um die Fernsteuerung und die Entscheidungen, die wir bewusst gegen naheliegende Abkürzungen getroffen haben. Alle Zahlen stammen aus dem Code, den Testprotokollen und der Git-Historie, Stand September 2026. Wo etwas noch offen ist, sagen wir das.

Die Windhunde-App auf einen Blick
Bevor wir ins Detail gehen, hier die Kurzfassung. Die Windhunde-App ist keine eigenständige App mit eigener Datenhaltung. Sie ist ein zweiter Zugang zu demselben System, das der Betreiber im Web-Dashboard nutzt. Jede Zahl, jede Zustandsfarbe und jede Frische-Angabe in der App kommt aus derselben Funktion wie im Web. Web und App können sich deshalb nicht widersprechen, weil es nur eine Quelle gibt. Das war die wichtigste Vorgabe des Auftraggebers: Das Dashboard sollte „1:1“ als App kommen, aber ausdrücklich ohne Web-Hülle. Wenn du selbst eine App entwickeln lassen willst, die an ein bestehendes System andockt, ist dieser Grundsatz der erste, über den wir mit dir sprechen würden.
Projekt-Steckbrief
Merkmal | Angabe |
|---|---|
Auftraggeber | Windhunde GmbH, Betreiber und Dienstleister für Windkraftanlagen |
Entwicklung | The Freelancer Marketing (Andy Staudinger) |
Branche | Windenergie: Betrieb, Überwachung und Wartung von Windkraftanlagen |
Produkt | Monitoring-Plattform aus Web-Dashboard, FastAPI-Backend, Datenerfassung und nativer App |
Plattformen | iOS und iPadOS (ab iOS 16.4), Android; Web-Dashboard im Browser |
Stack der App | React Native 0.86 mit Expo SDK 57 und Hermes, Expo Router, NativeWind, FlashList, react-native-sse, Reanimated, Expo Secure Store, Expo Local Authentication |
Stack der Plattform | Next.js 16, React 19, FastAPI, PostgreSQL mit TimescaleDB, SQLAlchemy 2, Alembic, Docker Compose, nginx, WireGuard |
Hosting | Self-hosted bei Hetzner in Deutschland, keine Managed Services |
Zeitraum laut Git | Erster Commit am 27.06.2026, App ab 21.09.2026, Stand dieser Case Study 23.09.2026 |
Umfang | 200 Commits, 31 Datenbank-Migrationen, 33 Backend-Testdateien, 10 App-Screens |
Status | App läuft auf Simulator und Testgeräten gegen die Produktivinstanz; Store-Einreichung in Vorbereitung |
Zur Plattform gehört mehr als die App. Das Herzstück ist ein Dienst, der die Anlagen rund um die Uhr abfragt, die Werte prüft und in einer Zeitreihen-Datenbank ablegt. Darauf sitzen eine Programmierschnittstelle, ein Web-Dashboard mit Drag-and-Drop-Layout und die App. Diese Case Study behandelt vor allem die App, erklärt aber die Teile darunter so weit, dass du verstehst, warum die App so gebaut ist, wie sie gebaut ist.
Was die App leistet
Die App deckt den Alltag eines Windpark-Betreibers ab, der nicht am Schreibtisch sitzt. Das sind die Funktionen, die heute im Code stehen:
Dashboard mit Live-Kennzahlen: Anlagen online, kritische Alarme, Anzahl der Anlagen im Stopp und ein Leistungstrend über alle Anlagen, wahlweise für 2, 5 oder 15 Minuten. Blöcke lassen sich per langem Druck und Ziehen umsortieren, das Layout wird je Nutzer gespeichert.
Anlagenliste: jede Anlage mit Zustand, Windpark, Typ und dem Alter des letzten Kontakts in Sekunden, durchsuchbar nach Anlage oder Windpark.
Anlagen-Detail: Leistung, Wind, Generator- und Rotordrehzahl und Pitch-Winkel live, dazu ein Spiegel des originalen Steuerungsdisplays im Turm.
Alarmzentrale: offene und erledigte Alarme mit Schweregrad, Anlage und Dauer.
Anlagen-Protokoll: Werteverlauf und Ereignisse je Anlage für einen wählbaren Zeitraum.
Anlagenregister: alle Anlagen mit Leistung, Anbindung, Sicht-Recht und dem Stand der Steuerungsfreigabe.
Sichere Anmeldung: E-Mail und Passwort, Zwei-Faktor-Authentifizierung mit Einmalcodes, danach Face ID oder Fingerabdruck.
Profil und Geräte: Rolle, Mandant, Zwei-Faktor-Status und eine Liste der angemeldeten Geräte, die sich einzeln abmelden lassen.
Fernsteuerung mit Schranken: eine Steuerkonsole im Anlagen-Detail, die nur mit Berechtigung, Freigabe der Anlage und biometrischer Bestätigung bedienbar ist.
Bewusst nicht in der ersten Version: der Datei-Export des Protokolls und die Wartungsanmeldung mit Kamera-Scan. Beides bleibt vorerst im Web. Warum, erklären wir weiter unten, denn jede dieser Lücken ist eine Entscheidung und kein Versehen.
Ausgangslage: Windkraftanlagen mit Daten von gestern
Viele Windparks in Deutschland bestehen aus Anlagen, die zehn, fünfzehn oder zwanzig Jahre alt sind. Sie laufen zuverlässig, aber ihre Steuerungstechnik stammt aus einer Zeit, in der niemand an ein Monitoring per App gedacht hat. Bei Windhunde steuert in jedem Turm eine kleine Box die Anlage, in unserem Fall ein Controller mit einem vierzeiligen Textdisplay. Auf diesem Display stehen Betriebsmodus, Status, Leistung, Drehzahlen, Wind und Pitch. Über ein Netzwerk gibt die Box genau diesen Displayinhalt als kleine XML-Datei heraus. Mehr nicht.
Das hat drei Folgen, die das ganze Projekt geprägt haben. Erstens hält die Box keine Historie. Sie kennt nur den aktuellen Wert. Was gestern um 14 Uhr passiert ist, weiß sie nicht. Vergangenheit existiert nur dort, wo jemand die Werte laufend abgeholt und gespeichert hat. Zweitens spricht die Box nur, wenn man sie fragt. Sie schickt keine Nachricht, wenn eine Störung auftritt. Drittens ist sie über Mobilfunk angebunden, weil ein Windpark selten einen Glasfaseranschluss hat. Jede Abfrage kostet also Datenvolumen und dauert spürbar länger als im lokalen Netz.
Vor dem Projekt sah der Alltag so aus: Wer wissen wollte, wie es einer Anlage geht, musste sich einzeln mit ihr verbinden oder auf einen Anruf warten. Ein Überblick über alle Anlagen gleichzeitig existierte nicht. Eine lückenlose Aufzeichnung, mit der man eine Störung im Nachhinein nachvollziehen kann, auch nicht. Und schon gar nicht auf dem Smartphone, also dort, wo Betreiber und Techniker tatsächlich sind: im Auto zwischen zwei Standorten, im Windpark selbst oder am Wochenende zu Hause.
Warum keine fertige SCADA-Lösung
Die naheliegende Frage lautet: Warum nicht einfach eine fertige SCADA-Software für Windkraftanlagen kaufen? Solche Systeme gibt es, meist vom Anlagenhersteller oder von spezialisierten Anbietern. Für diesen Fall passten sie aus mehreren Gründen nicht. Die Anlagen stammen aus verschiedenen Baureihen mit unterschiedlicher Firmware. Der Zugang zur Steuerbox gehört teilweise einem Dritten. Die Daten sollten auf einem eigenen Server in Deutschland liegen, ohne Cloud-Dienst eines Anbieters dazwischen. Und der Betreiber wollte die Oberfläche selbst bestimmen, bis hin zu einem eigenen Dashboard je Kunde. Eine maßgeschneiderte Lösung war hier nicht teurer, sondern die einzige, die alle Bedingungen erfüllt.
Die Rahmenbedingungen im Überblick
Bedingung | Was sie bedeutet |
|---|---|
Datenquelle ist das Display der Steuerbox | Werte müssen aus Text gelesen werden, nicht aus einer sauberen Schnittstelle |
Keine Historie in der Box | Jede Minute, die nicht abgeholt wird, ist für immer verloren |
Nur lesender Zugriff vereinbart | Monitoring darf die Anlage nie beeinflussen; Steuerung nur mit schriftlicher Freigabe |
Anbindung über Mobilfunk und VPN | Abbrüche, Zeitüberschreitungen und Datenvolumen sind der Normalfall |
Unterschiedliche Firmware und Sprachen | Die Displays sehen je Anlage anders aus, teils deutsch, teils englisch |
Self-hosted in Deutschland | Keine Managed Services, alle Daten auf eigenen Servern bei Hetzner |
Mehrere Kunden auf einer Plattform | Strikte Trennung der Daten je Mandant, eigene Rollen und Rechte |
Die Ziele des Projekts
Aus dieser Ausgangslage haben wir mit dem Auftraggeber fünf Ziele formuliert. Sie klingen einfach, aber jedes davon hatte Konsequenzen bis tief in die Architektur.
Alle Anlagen live auf einen Blick. Ein Dashboard zeigt sekundengenau, welche Anlage läuft, welche steht und wie viel Leistung der gesamte Park gerade erzeugt.
Lückenlose Aufzeichnung. Jeder Messwert, der sich ändert, wird gespeichert. So lässt sich jede Störung im Nachhinein rekonstruieren.
Alarme, die man nicht verpasst. Kritische Zustände erscheinen sofort in einer Alarmzentrale und später als Push-Nachricht auf dem Handy.
Dieselbe Wahrheit in Web und App. Keine zweite Rechenlogik in der App, keine abweichenden Werte, kein Streit darüber, welche Anzeige stimmt.
Sicherheit vor Bequemlichkeit. Eine App, die eine Windkraftanlage fernsteuern kann, ist ein Angriffsziel. Jede Funktion wird so gebaut, dass ein verlorenes Handy keinen Schaden anrichtet.
Eine Regel über allen anderen: keine erfundenen Daten
Eine Regel stand in diesem Projekt über allen anderen und steht bis heute ganz oben in den Projektvorgaben: keine erfundenen Daten. Das bedeutet keine geschätzten Kennzahlen, keine Trendpfeile ohne Grundlage und keine aufgefüllten Lücken in Diagrammen. Wenn eine Anlage seit drei Minuten nichts geliefert hat, zeigt die App nicht den letzten bekannten Wert, als wäre er aktuell. Sie zeigt, dass der Wert drei Minuten alt ist. Wenn das Impressum noch nicht vollständig hinterlegt ist, schreibt die App genau das hin, statt Platzhalter anzuzeigen. Das klingt nach einer Kleinigkeit, hat aber das Design fast jedes Screens beeinflusst. Monitoring, dem man nicht trauen kann, ist schlimmer als gar kein Monitoring.
Eine Anzeige, die einen alten Wert als aktuell darstellt, ist gefährlicher als eine leere Anzeige. Die leere merkt man, die falsche nicht.
Die Grundsatzentscheidungen vor der ersten Zeile App-Code
Die App kam nicht am Anfang, sondern am Ende. Zuerst entstanden Datenerfassung, Datenbank, Schnittstelle und Web-Dashboard. Erst als das Web-Dashboard mit echten Anlagen stabil lief, kam der Auftrag für die App. Das war ein Vorteil: Wir mussten nicht raten, was die App anzeigen soll. Wir wussten es, weil es im Web schon funktionierte. Vor dem ersten Screen haben wir fünf Entscheidungen schriftlich festgehalten.
Entscheidung | Begründung |
|---|---|
Reines React Native mit Expo, keine Web-Hülle | Der Auftraggeber hat WebView, Capacitor und eingebettete Web-Screens ausdrücklich ausgeschlossen. Eine echte App reagiert schneller, fühlt sich nativ an und kommt durch die Store-Prüfung |
Einreichung in App Store und Google Play ist Pflicht | Keine Dauerlösung über Testverteilung oder Sideload. Das Ziel ist eine App, die jeder Kunde im Store findet |
„1:1“ heißt gleiche Daten, nicht gleicher UI-Code | Die Oberfläche wird für das Handy neu gebaut. Gemeinsam sind die Rechen- und Anzeigelogik in einem geteilten Paket |
Ein Repository für Web, App und Backend | Die App liegt unter apps/mobile, die geteilte Logik unter packages/shared. Eine Änderung an einer Formel wirkt sofort überall |
Alle bestehenden Projektregeln gelten weiter | Mandantentrennung, Aufbewahrungsfristen aus der Datenbank, keine erfundenen Werte und ein Verifikations-Loop gegen echte Daten als Definition of Done |
Warum React Native und nicht Flutter oder zwei native Apps? Die Antwort ist pragmatisch. Das Web-Dashboard ist in TypeScript und React geschrieben. Mit React Native konnten wir die gesamte Anzeige- und Rechenlogik als TypeScript-Paket teilen, statt sie in Dart oder Swift und Kotlin neu zu schreiben. Das ist nicht nur schneller, es verhindert auch, dass zwei Implementierungen derselben Formel auseinanderlaufen. Einen ausführlichen Vergleich der Frameworks findest du in unserem Guide zur React-Native-App-Entwicklung und im Gegenstück zur Flutter-App-Entwicklung. Für Projekte, bei denen schon ein React-Web-Frontend existiert, ist React Native fast immer die wirtschaftlichere Wahl.
Auch die Wahl von Expo war bewusst. Expo liefert einen gemeinsamen Build- und Einreichungsweg für beide Stores, eine dateibasierte Navigation, die der Struktur des Web-Dashboards entspricht, und fertige Module für sichere Ablage, Biometrie, Kamera und Push. Das spart Wochen an Handarbeit in Xcode und Gradle. Gleichzeitig bleibt die App eine echte native App, die sich jederzeit mit eigenem nativen Code erweitern lässt.
Die Datenquelle: ein vierzeiliges Display als Schnittstelle
Jede App ist nur so gut wie die Daten, die sie anzeigt. Deshalb beginnt die Geschichte der Windhunde-App nicht im App-Code, sondern in der Steuerbox im Turm. Diese Box gibt über das Netzwerk eine kleine XML-Datei heraus. Sie ist rund 600 Byte groß und spiegelt das vierzeilige Textdisplay des Controllers. Es gibt fünf Einträge: die Kopfzeile mit Menü, Modus, Datum und Uhrzeit, eine Statuszeile, eine Legendenzeile mit den Spaltenköpfen, eine Zeile mit den Messwerten und einen achtstelligen Code für die Statuslampen.
Das ist keine Schnittstelle im heutigen Sinn. Es gibt kein JSON, keine Feldnamen wie power oder windSpeed und keine Versionsnummer. Es gibt eine Zeile Text, in der zum Beispiel „-7.8kW 6.9UpM 0.1UpM 3.8m/s 88.7°“ steht. Die Kunst besteht darin, aus diesem Text zuverlässig Zahlen zu machen, und zwar für jede Anlage, jede Firmware und jede Sprache.
Fünf Fallstricke, die wir im Parser abfangen
Beim Aufbau des Parsers sind wir auf fünf Probleme gestoßen, die in keiner Dokumentation standen. Jedes davon hätte ohne Gegenmaßnahme still falsche Werte erzeugt.
Zeichensatz. Die Datei ist als ISO-8859-1 deklariert, nicht als UTF-8. Umlaute wie im Menütitel „ÜBERBLICK“ und das Gradzeichen beim Pitch-Winkel kommen als Latin-1-Bytes. Wer das mit UTF-8 liest, bekommt Zeichensalat oder einen Abbruch.
Menü statt Messwerte. Das Display zeigt nicht immer die Messwerte. Blättert ein Techniker vor Ort durch das Menü, stehen in derselben Zeile ganz andere Zahlen. Der Parser prüft deshalb zuerst, ob die Legendenzeile die Spaltenköpfe zeigt. Nur dann gelten die Zahlen als Messwerte. Sonst wird die Rohzeile gespeichert, aber kein Messwert übernommen.
Zwei Firmware-Generationen. Neuere Boxen zeigen Leistung, Generator- und Rotordrehzahl, Wind und Pitch. Ältere zeigen ein „Live-Display“ mit Leistung, nur einer Drehzahl und Wind, aber ohne Pitch. Der Parser erkennt beide Layouts in fester Reihenfolge.
Zwei Sprachen. Manche Boxen sind auf Deutsch eingestellt, andere auf Englisch. Aus „Leist.“ wird „Power“, aus „UpM“ wird „rpm“. Ein Parser, der nur eine Sprache kennt, verwirft die anderen Anlagen still, und genau das wäre nicht aufgefallen.
Eine Drehzahl, aber welche? Liefert eine ältere Box nur eine Drehzahl, ordnet der Parser sie über die Größenordnung ein: ab 100 Umdrehungen pro Minute ist es der Generator, darunter der Rotor. Die andere Spalte bleibt leer. Sie wird nicht geschätzt.
Dazu kommen die Kleinigkeiten, die einen Parser robust machen: variable Leerzeichen, negative Leistung bei Eigenverbrauch und Statuszeilen, die selbst Doppelpunkte enthalten und deshalb nur am ersten Doppelpunkt geteilt werden dürfen. Jede dieser Regeln ist mit einem Test abgesichert, der ein echtes Display-Beispiel verwendet. Wenn du wissen willst, wie wir solche Altsysteme grundsätzlich anbinden, lies unsere Seite zur Backend- und API-Entwicklung.
Die acht Statuslampen
Neben den Werten liefert die Box einen achtstelligen Code aus Nullen und Einsen. Jede Stelle steht für eine Lampe am Controller. Die Bedeutung stand nirgends geschrieben. Wir haben sie gemeinsam mit dem Betreiber an einer laufenden Anlage bestätigt. Die erste Stelle steht für „Run“ und ist die einzige grüne Lampe. Die übrigen sieben stehen für Pause, Drehrichtung, Azimut-Stopp, Funktionstasten und Sonderzeichen und leuchten rot. Die App zeigt diesen Code im Anlagen-Detail so, wie er an der Box steht. Techniker, die die Anlage kennen, erkennen ihn sofort wieder.
Die Datenerfassung: alle zwei Sekunden, aber nur bei Änderung
Weil die Box keine Historie hält, muss jemand sie ständig fragen. Das übernimmt ein eigener Dienst, der Poller. Er läuft getrennt vom Rest der Plattform, fragt jede aktive Anlage alle zwei Sekunden ab und schreibt einen neuen Messwert nur dann, wenn sich etwas geändert hat. Dafür nutzt er Conditional GET: Er schickt mit jeder Anfrage die Kennung der letzten Antwort mit. Hat sich die Datei nicht geändert, antwortet die Box mit dem HTTP-Code 304 und ohne Inhalt, und es wird kein neuer Datensatz angelegt.
Der Poller ist nach Regeln gebaut, die für jede Anbindung über Mobilfunk und VPN gelten sollten. Jeder Abruf hat eine Zeitgrenze, damit eine hängende Verbindung nicht den ganzen Dienst blockiert. Fehlgeschlagene Abrufe werden mit exponentiell wachsendem Abstand und einer zufälligen Streuung wiederholt, damit nicht alle Anlagen gleichzeitig neu anfragen. Jede Anlage läuft isoliert: Eine Box, die nicht antwortet, bremst die anderen nicht aus. Und der Import ist idempotent, das heißt, derselbe Zustand erzeugt nie zwei Datensätze.
Eigenschaft | Umsetzung im Poller |
|---|---|
Abfrageintervall | 2 Sekunden je Anlage, per Konfiguration änderbar |
Datensparen | Conditional GET mit ETag, bei unveränderter Datei nur HTTP 304 ohne Inhalt |
Speichern | Nur bei echter Änderung eines Werts |
Zeitgrenzen | Für jeden Abruf, keine unbegrenzten Hänger |
Wiederholung | Exponentieller Backoff mit Jitter |
Fehler-Isolation | Je Anlage, eine gestörte Box beeinflusst keine andere |
Veraltete Werte | Anlage gilt nach einer einstellbaren Frist ohne Kontakt als veraltet |
Anlagen finden | Ersteinrichtung aus einer Übersichtsseite oder einer CSV-Datei, idempotent |
Warum Datensparen hier anders funktioniert als gedacht
Im Projekt kam die Frage auf, wie viel Datenvolumen die Überwachung über Mobilfunk verbraucht. Die Antwort war lehrreich. Eine XML-Datei ist nur 600 Byte groß, aber jede Abfrage kostet zusätzlich Kopfzeilen für Anfrage und Antwort und den Overhead des VPN-Tunnels. Diese Fixkosten fallen auch an, wenn die Box nur mit 304 antwortet. Sie sind sogar größer als die eigentliche Nutzlast. Conditional GET spart deshalb höchstens rund ein Drittel, nicht die erhofften neunzig Prozent.
Szenario bei 2 Sekunden und 13 Anlagen | Volumen je Monat |
|---|---|
Jede Abfrage liefert neue Daten | rund 26,3 GB |
Realistisch, etwa jede fünfte Abfrage geändert | rund 18,2 GB |
Sehr ruhiger Betrieb, etwa jede zwanzigste geändert | rund 16,7 GB |
Zielbild: Router bündelt und sendet einmal pro Minute | rund 0,8 GB |
Zielbild: Router sendet alle fünf Minuten | rund 0,16 GB |
Die Lösung liegt deshalb nicht im Poller, sondern eine Ebene tiefer. Die Mobilfunk-Router in den Windparks gehören dem Betreiber, und auf ihnen lassen sich eigene Skripte ausführen. Künftig fragt der Router die Box lokal über das Netzwerk im Turm ab, das kostet kein Mobilfunkvolumen. Er sammelt die Zwei-Sekunden-Werte und schickt sie gebündelt an den Server. So bleibt die volle Auflösung erhalten, und das Volumen sinkt um rund 96 Prozent. Diese Rechnung haben wir vor der Entscheidung aufgestellt, nicht danach. Sie zeigt, warum IoT-Monitoring über Mobilfunk immer mit einer Volumenrechnung beginnen sollte.
Speicherung: PostgreSQL mit TimescaleDB, selbst betrieben
Alle Messwerte landen in einer PostgreSQL-Datenbank mit der Erweiterung TimescaleDB. TimescaleDB ist für Zeitreihen gebaut, also genau für Daten, bei denen jede Zeile einen Zeitpunkt hat und Abfragen fast immer über Zeiträume laufen. Die Datenbank läuft selbst betrieben auf dem Server bei Hetzner. Einen Managed Service gibt es in diesem Projekt bewusst nicht, weder für die Datenbank noch für irgendetwas anderes.
Jeder Messwert enthält Leistung in Kilowatt, Generator- und Rotordrehzahl, Windgeschwindigkeit, Pitch-Winkel, den Abrufzeitpunkt, den Merker, ob die Werte aus der Übersicht stammen, und die Zuordnung zu Anlage und Mandant. Kennzahlen und Diagramme werden nur aus Zeilen berechnet, die aus der Übersicht stammen. Zeilen aus dem Menü werden gespeichert, damit man sieht, dass vor Ort jemand am Controller war, aber sie fließen in keinen Mittelwert ein.
Das Schema wird ausschließlich über versionierte Migrationen mit Alembic verwaltet, bis heute 31 Stück. Jede Migration muss sich hoch, wieder zurück und erneut hoch spielen lassen, bevor sie als fertig gilt. Die Anwendung selbst läuft zur Laufzeit mit einem Datenbanknutzer, der nur Daten lesen und schreiben darf, aber keine Tabellen ändern kann. Tabellen ändern darf nur ein getrennter Migrationsnutzer. Das ist das Prinzip der geringsten Rechte, und es kostet in der Umsetzung fast nichts. Mehr zu solchen Architekturen findest du auf unserer Seite zur Datenbank-Entwicklung.
Aufbewahrung ist Konfiguration, nicht Code
Wie lange Messwerte, Ereignisse und Personendaten aufbewahrt werden, steht nirgends im Code. Die Fristen liegen in einer eigenen Tabelle und lassen sich ohne Update ändern. Für pflichtige Daten gibt es eine Untergrenze, die sich nicht unterschreiten lässt. Technische Daten und Personendaten sind getrennt: Namensfelder lassen sich einzeln löschen oder pseudonymisieren, ohne dass der technische Verlauf einer Anlage verloren geht. Eine rechtliche Sperre, der Legal Hold, blockiert jede Löschung, solange sie gesetzt ist. So lassen sich Aufbewahrungspflichten und Datenschutz gleichzeitig erfüllen.
Live-Übertragung: Snapshot, dann Deltas
Wie kommt ein Messwert aus der Datenbank in Echtzeit auf das Handy? Viele Apps fragen dafür alle paar Sekunden den Server ab. Das ist einfach, aber es verschwendet Akku und Daten und ist trotzdem nie ganz aktuell. Die Windhunde-Plattform macht es anders. Sie nutzt Server-Sent Events, kurz SSE, also eine dauerhaft offene Verbindung, über die der Server Neuigkeiten schickt, sobald sie entstehen.
Die Mechanik dahinter ist elegant. Nach jedem neuen Messwert sendet der Import-Dienst ein Signal innerhalb der Datenbank, in PostgreSQL heißt das LISTEN und NOTIFY. Der Stream-Dienst hört auf dieses Signal. Öffnet die App eine Verbindung, bekommt sie zuerst einen vollständigen Schnappschuss aller Anlagen. Danach kommen nur noch die Änderungen, die Deltas. Frontend-Polling gibt es seitdem weder im Web noch in der App.
Schritt | Was passiert |
|---|---|
1. Box ändert Werte | Der Poller holt die neue XML-Datei und prüft sie |
2. Speichern | Der neue Messwert landet in der Zeitreihen-Datenbank |
3. Signal | Die Datenbank meldet per NOTIFY, dass es einen neuen Wert gibt |
4. Stream | Der Stream-Dienst schickt das Delta an alle verbundenen Geräte des Mandanten |
5. Anzeige | App und Web aktualisieren genau diese eine Anlage |
Warum SSE in der App besser ist als im Browser
Im Browser hat SSE eine lästige Einschränkung: Man kann keinen eigenen Header mitschicken, also auch kein Zugangstoken. Das Web-Dashboard nutzt deshalb Cookies. Eine native App hat dieses Problem nicht. Mit der Bibliothek react-native-sse schickt die App ihr Zugangstoken ganz normal im Header mit. Auch die Auswahl des Mandanten läuft über einen Header, statt über einen Umweg in der Adresse. Die App nutzt dieselbe Stream-Schnittstelle wie das Web, nur mit einem saubereren Transport.
Vordergrund, Hintergrund und die Leiste, die ehrlich ist
Auf dem Handy gibt es eine Besonderheit, die im Browser kaum auffällt. iOS und Android beenden Netzwerkverbindungen, sobald eine App in den Hintergrund geht. Sie fragen nicht, und sie melden es auch nicht. Wer das ignoriert, baut eine App, die nach dem Zurückkehren eingefrorene Werte zeigt, die genauso aussehen wie aktuelle. Im Monitoring ist das der gefährlichste Fehler überhaupt.
Die Windhunde-App geht deshalb offen damit um. Geht sie in den Hintergrund, schließt sie den Stream sauber. Kommt sie zurück, verbindet sie sich neu und holt einen frischen Schnappschuss. Bis der da ist, zeigt sie einen Ladezustand und nicht den letzten bekannten Wert. Wechselt das Handy vom WLAN ins Mobilfunknetz, verbindet sie sich sofort neu. Und oben auf jedem Live-Screen steht eine Leiste mit dem Zustand der Verbindung, zum Beispiel „Live · Stand vor 1 s“. Ein Takt von einer Sekunde aktualisiert das Alter jedes Werts. Nach 60 Sekunden ohne neue Daten gilt ein Wert als veraltet und wird entsprechend markiert.

Diese Leiste ist nicht Zierde. Sie ist die Konsequenz aus einem echten Vorfall im Web-Dashboard, bei dem eine Verbindung unbemerkt abgerissen war und die Anzeige eingefroren blieb. Seitdem gilt im ganzen Projekt: Kein stilles Schlucken von Fehlern, weder im Server noch in der Oberfläche. Wenn etwas nicht stimmt, sieht man es. Dieser Grundsatz ist bei jeder Echtzeit-App wichtig, aber bei der Überwachung von Anlagen, die im Störfall Schaden nehmen können, ist er Pflicht.
Eine Wahrheit für Web und App: das geteilte Paket
„1:1 zum Web“ war der Auftrag. Die naive Umsetzung wäre gewesen, das Web-Dashboard in einer WebView einzupacken. Das war ausdrücklich ausgeschlossen. Die zweitnaivste wäre gewesen, alles für die App neu zu schreiben, inklusive aller Formeln. Dann hätte es nach wenigen Monaten zwei Versionen der Wahrheit gegeben, die langsam auseinanderlaufen. Wir haben einen dritten Weg gewählt.
Bevor die erste Zeile App-Code entstand, haben wir die gesamte Anzeige- und Rechenlogik aus dem Web-Dashboard herausgelöst und in ein eigenes Paket verschoben, packages/shared. Web und App importieren dieses Paket. Darin liegen die Formatierung von Zahlen und Einheiten, die Regeln für die Frische eines Werts, die Zuordnung von Zuständen zu Farben, die Schemata für die Daten der Schnittstelle, der API-Client mit Wiederholungslogik und die Zustandsmaschine für die Zwei-Faktor-Anmeldung. Auch der Kern der Live-Verbindung liegt dort: das Zusammenführen von Schnappschuss und Deltas, die Regeln für Wiederverbindung und die Erkennung einer stehengebliebenen Verbindung.
Für dieses Paket gilt eine strenge Regel: kein Zugriff auf Browser-Objekte wie window, document oder localStorage und kein fest eingebauter Netzwerkaufruf. Alles, was vom Gerät abhängt, wird von außen hineingereicht. Im Web ist das der Browser-Stream, in der App die native Stream-Bibliothek. Ein Test lädt das Paket unter Node ganz ohne Browser-Umgebung. Wenn dieser Test grün ist, läuft der Code in beiden Welten. Die Extraktion galt erst als fertig, als das Web danach unverändert lief, ohne eine einzige neue Warnung.
Was geteilt wird | Was das bringt |
|---|---|
Formatierung von Zahlen und Einheiten | Eine Leistung von 1.050 kW heißt in Web und App gleich „1,05 MW“ |
Frische-Regeln | Beide zeigen dieselbe Sekunde, ab der ein Wert als veraltet gilt |
Zustand und Farbe je Anlage | Eine Anlage im Stopp ist überall gleich markiert |
Schemata der Schnittstelle | Falsche oder fehlende Felder fallen sofort auf, nicht erst beim Nutzer |
API-Client mit Token-Erneuerung | Gleiches Verhalten bei abgelaufener Sitzung, gleiche Fehlercodes |
Kern der Live-Verbindung | Gleiche Reihenfolge von Schnappschuss und Deltas, gleiche Wiederverbindung |
Design-Tokens | Dieselben Farben, Radien und Schriften, aus einer Quelle erzeugt |
Design-Tokens aus einer Quelle
Das Web-Dashboard definiert sein Aussehen über rund hundert Design-Tokens, also benannte Werte für Farben, Diagrammfarben, Radien und Schriften. Diese Werte werden nicht in die App abgetippt. Ein Skript erzeugt aus der Stildatei des Webs eine TypeScript-Datei, und die App liest daraus ihre Farben über NativeWind. NativeWind bringt die Klassensyntax von Tailwind CSS nach React Native. Wer im Web eine Farbe ändert und das Skript laufen lässt, ändert sie in der App mit. Der Dunkelmodus folgt wie im Web der Einstellung des Systems. Die Primärfarbe ist das Windhunde-Blau, derselbe Hex-Wert wie im Web.
Auch das Vokabular der Oberfläche ist gleich. Im Web nutzen wir shadcn/ui, in der App die Portierung mit denselben Komponentennamen. Ein Hinweisschild heißt in beiden Welten gleich und hat dieselben Varianten. Die Symbole kommen aus Lucide, im Web als React-Paket, in der App als React-Native-Paket, Symbol für Symbol identisch. Wer das Web kennt, findet sich in der App sofort zurecht, obwohl kein einziges Stück Oberflächencode geteilt ist.
Navigation: Tabs unten, Seitenmenü wie im Web
An einer Stelle weicht die App bewusst vom Web ab: bei der Navigation. Das Web-Dashboard hat eine Seitenleiste links. Auf einem 390 Pixel breiten Handybildschirm ist eine feste Seitenleiste unbenutzbar. Die App nutzt deshalb unten eine Tab-Leiste mit fünf Einträgen, so wie es Nutzer von iOS und Android gewohnt sind.
Tab | Inhalt |
|---|---|
Dashboard | Kennzahlen, Leistungstrend und Betriebsübersicht, live |
Anlagen | Liste aller Anlagen mit Zustand und Kontaktalter, Zugang zu Register und Protokoll |
Alarme | Alarmzentrale mit offenen und erledigten Alarmen |
Wartung | An- und Abmeldung vor Ort, vorerst über die bestehende Webseite |
Mehr | Profil, Zwei-Faktor, angemeldete Geräte, Impressum und Abmelden |
Zusätzlich gibt es ein Seitenmenü, das wie im Web auf schmalen Bildschirmen von links hereinfährt. Man öffnet es über den Menüknopf oder mit einem Wisch vom linken Rand. Einträge, Reihenfolge, Rechte und Fußzeile sind dieselben wie im Web. Jeder Eintrag ist nur sichtbar, wenn die Rolle ihn nutzen darf. Die Sichtbarkeit ist dabei reine Oberfläche: Jede Route prüft die Rechte zusätzlich auf dem Server. Und das Menü zeigt nur Einträge, hinter denen wirklich ein fertiger Screen steht. Die Nutzerverwaltung gibt es im Web, aber noch nicht in der App. Deshalb fehlt der Eintrag in der App, statt auf einen leeren Bildschirm zu führen.

Die Navigation selbst läuft über Expo Router. Er ist dateibasiert wie das App-Verzeichnis von Next.js. Die Struktur der Routen in der App entspricht deshalb der Struktur im Web, was die Wartung deutlich vereinfacht. Jeder Screen hat außerdem eine eigene Adresse, über die er sich direkt öffnen lässt. Das ist die Grundlage dafür, dass eine Push-Nachricht später direkt den richtigen Alarm öffnet.
Die Screens im Detail
Die App besteht aus zehn Screens: fünf Tabs, das Anlagen-Detail, das Anlagen-Protokoll, das Anlagenregister, die Anmeldung und die Einrichtung des zweiten Faktors. Hier stellen wir die wichtigsten vor und erklären jeweils, warum sie so aussehen, wie sie aussehen. Die Screenshots zeigen einen Demo-Mandanten mit Demo-Anlagen.
Dashboard: Kennzahlen ohne Schönfärberei
Das Dashboard ist der erste Screen nach der Anmeldung. Oben steht die Live-Leiste mit dem Stand der Verbindung. Darunter folgen vier Kennzahlkarten in einer waagerecht scrollbaren Reihe: Anlagen online, kritische Alarme, Anlagen im Stopp und der störungsfreie Betrieb. Jede Karte hat dieselben Beschriftungen, Fußnoten und Symbole wie im Web. Fehlt ein Wert, steht dort ein Gedankenstrich. Es gibt keine Trendpfeile und keine Prozentangaben gegenüber gestern, weil es dafür keine belastbare Grundlage gibt.
Die Reihenfolge der Karten und der Blöcke darunter kommt aus dem gespeicherten Layout des Nutzers. Wie im Web lässt sie sich per Drag and Drop ändern: lange drücken hebt ein Element an, Ziehen verschiebt es, Loslassen legt die neue Reihenfolge fest. Die Nachbarn rücken dabei live zur Seite, damit man sieht, wo das Element landet. Während des Ziehens ist das Scrollen der Liste abgeschaltet, sonst würden Finger und Liste um dieselbe Bewegung streiten. Gespeichert wird am Konto, nicht am Gerät. Wer auf dem Handy umsortiert, sieht dieselbe Reihenfolge im Web.
Leistungstrend: Lücken bleiben Lücken
Der Leistungstrend zeigt die Produktion aller Anlagen oder einer einzelnen Anlage für die letzten 15, 5 oder 2 Minuten. Die Kurve startet mit echten Werten aus der Historie und wächst dann aus dem Live-Stream weiter. Die Zeitachse endet am letzten echten Messpunkt, nicht an der Uhr des Geräts. Sonst würde eine hängende Verbindung wie ein Leistungsabfall aussehen. Die Werteachse ist auf 50er-Schritte gerundet, damit alte Punkte nicht springen, wenn ein neuer Höchstwert kommt.
Die wichtigste Regel des Diagramms ist unsichtbar, solange alles läuft: Lücken bleiben Lücken. Fällt eine Messung aus, verbindet das Diagramm die Punkte davor und danach nicht. Ein Ausfall darf nicht wie ein gleichmäßiger Verlauf aussehen. Außerdem sind ein Serverfehler und „keine Messwerte im Zeitraum“ zwei verschiedene Anzeigen, weil sie zwei verschiedene Dinge bedeuten. Gezeichnet wird mit react-native-svg und einer monoton-kubischen Kurve, wie im Web. Zum Ablesen legt man den Finger auf die Fläche, und ein Fadenkreuz zeigt Zeit und Wert des nächsten Messpunkts.
Anlagenliste: Kontaktalter statt Ampel allein
Die Anlagenliste zeigt jede Anlage als Karte: Name, Windpark, Typ, Zustand und wie lange der letzte Kontakt her ist, zum Beispiel „Letzter Kontakt vor 0 s“. Oben sucht man nach Anlage oder Windpark. Tabellen wie im Web gibt es auf dem Handy nicht, sie hätten dort keinen Platz. Stattdessen nutzt die App Kartenlisten mit FlashList, einer Listenkomponente, die auch sehr lange Listen flüssig darstellt. Das Kontaktalter ist bewusst so prominent. Eine grüne Markierung allein sagt nichts darüber, ob der Wert von vor einer Sekunde oder von vor einer Stunde stammt.

Anlagen-Detail: das Original-Display in der Hosentasche
Tippt man auf eine Anlage, öffnet sich das Detail. Oben stehen die Messwerte als große Zahlen: Leistung, Wind, Generator- und Rotordrehzahl und Pitch. Darunter kommt etwas, das Techniker besonders schätzen: ein Spiegel des Original-Displays der Steuerbox, Zeile für Zeile, wie es im Turm aussieht. Dazu kommen die acht Statuslampen und der Zeitpunkt der letzten Aktualisierung. Wer die Anlage seit Jahren kennt, liest dieses Display schneller als jede neu gestaltete Anzeige. Wir haben es deshalb nicht ersetzt, sondern ergänzt.

Unter dem Display liegt die Steuerkonsole mit denselben Tasten wie an der Box. Sie ist nur für Rollen mit Steuerrecht sichtbar, und auch dann nur bedienbar, wenn drei Bedingungen auf dem Server erfüllt sind und das Handy per Face ID oder Fingerabdruck freigeschaltet wurde. Wie das im Detail funktioniert, erklären wir im nächsten Teil, denn die Fernsteuerung ist der sensibelste Teil der ganzen Plattform.
Alarmzentrale: bewusst nicht am Live-Stream
Die Alarmzentrale zeigt offene Alarme oben und erledigte darunter, jeweils mit Anlage, Schweregrad und Dauer, etwa „Kritisch, seit 37 min, behoben“. Anders als das Dashboard hängt sie nicht am Live-Stream. Alarme sind einzelne Ereignisse, keine fortlaufenden Messwerte. Sie werden abgerufen und künftig per Push-Nachricht zugestellt. Der Grund ist einfach: Ein Alarm, der nur sichtbar ist, solange die App offen im Vordergrund liegt, ist für eine Leitstelle nutzlos. Die Push-Zustellung ist im Backend bereits vollständig gebaut. Es fehlen nur noch die Zugangsdaten für die Push-Dienste von Apple und Google, die an den Store-Konten hängen.

Anlagen-Protokoll und Anlagenregister
Im Anlagen-Protokoll wählt man eine Anlage und einen Zeitraum, zum Beispiel die letzten 24 Stunden. Die App zeigt Ereignisse und den Werteverlauf. Bei mehr als tausend Messwerten zeigt sie die neuesten tausend und sagt das auch ausdrücklich dazu. Den vollständigen Bestand liefert der Export im Web-Dashboard als CSV oder PDF. Der Export fehlt in der App bewusst, weil Datei-Handling auf dem Handy ein eigenes Projekt wäre und das Web diese Aufgabe gut erfüllt.

Das Anlagenregister listet alle Anlagen des Mandanten mit aktueller Leistung, Online-Status, Windpark, Typ und Art der Anbindung. Für jede Anlage steht dort außerdem, ob die Steuerung freigegeben ist und von wem. Das Register ist damit zugleich die Stelle, an der man auf einen Blick sieht, welche Anlagen überhaupt aus der Ferne bedient werden dürfen.

Mehr: Profil, Geräte und ein ehrliches Impressum
Unter „Mehr“ stehen Name, E-Mail, Rolle und Mandant des angemeldeten Nutzers, der Stand der Zwei-Faktor-Anmeldung und eine aufklappbare Liste der angemeldeten Geräte. Jedes Gerät lässt sich einzeln abmelden, auch aus dem Web heraus. Wer sein Handy verliert, meldet es am Rechner ab, und der Zugang auf dem verlorenen Gerät ist sofort ungültig. Darunter steht das Impressum. Es kommt als Datensatz vom Server, damit Web und App denselben Stand zeigen und eine Änderung kein App-Update braucht. Solange Pflichtangaben fehlen, sagt die App genau das, statt Ersatzangaben zu zeigen.

Wartung: lieber gar nicht als halb
Der Tab Wartung zeigt eine Besonderheit. Für Wartungsteams gibt es im Web eine Anmeldung vor Ort: Wer an einer Anlage arbeitet, meldet sich über einen QR-Code am Windpark an und nach der Arbeit wieder ab. Alles landet im Anlagen-Protokoll. In der App soll diese Anmeldung mit Kamera-Scan der Anlagennummer und einem Offline-Entwurf kommen, der auch ohne Netz im Turm funktioniert. Bis das fertig ist, führt der Tab auf die bestehende Webseite. Wir haben bewusst keine halbe Version gebaut. Ein Anmeldeweg vor Ort, der nur manchmal funktioniert, wäre schlimmer als gar keiner.
Start-Animation und Marke je Kunde
Die Plattform wird von zwei Partnern betrieben, die Windhunde GmbH und ein Ingenieurbüro für Windenergie. Beim Kaltstart zeigt die App deshalb eine kurze Animation mit beiden Marken. Sie beginnt pixelgleich mit dem nativen Startbildschirm, also mit gleicher Größe, gleicher Mitte und gleicher Farbe. Erst wenn die Animation selbst gezeichnet ist, blendet sie den nativen Bildschirm aus. Der Übergang ist dadurch unsichtbar. Danach schrumpft das Windhunde-Logo, eine Trennlinie wächst aus der Mitte, das zweite Logo gleitet herein, und nach etwa zweieinhalb Sekunden öffnet sich die App. Wer in den Bedienungshilfen „Bewegung reduzieren“ eingeschaltet hat, sieht beide Logos sofort, ohne Bewegung.
Hinter der Animation steckt ein größeres Thema: White Label. Die Plattform soll künftig an weitere Kunden verkauft werden, jeder mit eigenem Dashboard, eigener Adresse, eigenem Logo und eigenem Namen. Die App liest die Marke des angemeldeten Nutzers vom Server und zeigt sie im Menükopf, beim Entsperren und im Impressum. Fehlt eine Marke, gilt der Name des Mandanten, sonst der der Plattform. Wenn du eine Web-Applikation als SaaS für mehrere Kunden planst, ist diese Trennung von Plattform und Marke von Anfang an viel günstiger als nachträglich.
Anmeldung: dieselben Endpunkte, ein anderer Transport
Das Web-Dashboard arbeitet mit Cookies, die nur der Server lesen kann. Das ist im Browser der sichere Standard. Eine native App kann mit solchen Cookies aber wenig anfangen. Die naheliegende Lösung wäre ein zweiter Anmeldeweg nur für die App gewesen. Wir haben das Gegenteil gemacht und denselben Anmeldeweg um eine Option erweitert.
Die App schickt bei der Anmeldung einen Hinweis mit, dass sie die Zugangstoken im Antwortkörper haben möchte statt als Cookie. Ohne diesen Hinweis bleibt alles wie bisher, das Web ist also unberührt. Auf dem Server prüft eine einzige Funktion, ob ein Nutzer angemeldet ist, und sie akzeptiert entweder das Cookie oder das Token im Header. Ein Ort, eine Prüfung, dieselben Fehlercodes. Dass sich am Web nichts geändert hat, belegen die 304 bestehenden Anmeldetests, die nach der Erweiterung unverändert grün waren. Mehr zu dieser Art von Schnittstellen findest du unter Backend- und API-Entwicklung.
Kurzlebige Zugangstoken, geschützte Erneuerung
Die App arbeitet mit zwei Token. Das Zugangstoken lebt 15 Minuten und wird nur im Arbeitsspeicher gehalten, nie auf dem Gerät gespeichert. Es zu speichern würde nur das Zeitfenster verlängern, in dem es jemand finden kann, ohne etwas zu sparen. Das Erneuerungstoken ist der eigentliche Zugang, denn mit ihm lassen sich neue Zugangstoken holen. Es liegt deshalb im Schlüsselbund des Geräts, also im iOS Keychain oder im Android Keystore. In den normalen App-Speicher kommt es nicht, weil dessen Inhalt als lesbare Datei im App-Verzeichnis liegt. Bei jeder Erneuerung wird das Token ausgetauscht. Ein abgefangenes altes Token ist danach wertlos.
Element | Wo es liegt | Warum |
|---|---|---|
Zugangstoken | Nur im Arbeitsspeicher, 15 Minuten gültig | Speichern bringt keinen Vorteil, nur Risiko |
Erneuerungstoken | Keychain oder Keystore, freigegeben per Biometrie | Der eigentliche Zugang, muss am besten geschützt sein |
TOTP-Geheimnis (optional) | Keychain oder Keystore, lesbar nur mit Face ID oder Touch ID | Die App kann selbst zweiter Faktor sein, aber nur mit Biometrie |
Passwort | Nirgends | Wird nach der Anmeldung nicht aufbewahrt |
Zwei-Faktor-Anmeldung in einer Maske
Die Plattform verlangt auf Wunsch einen zweiten Faktor, einen sechsstelligen Code, der alle 30 Sekunden wechselt. Auf ein korrektes Passwort antwortet der Server dann nicht mit einer Sitzung, sondern mit der Aufforderung, den Code einzugeben. In der App ist das kein eigener Bildschirm, sondern ein zweiter Schritt derselben Maske. Der Grund ist kein Designgeschmack: Wer zwei Bildschirme baut, muss das Passwort zwischen ihnen zwischenlagern, und genau das soll nicht passieren. Für den Fall, dass das Gerät mit dem Code verloren ist, gibt es Wiederherstellungscodes.
Die App kann außerdem selbst als Authenticator dienen. Dann liegt das Geheimnis für die Codes im Schlüsselbund und wird nur nach Face ID oder Touch ID gelesen. Ohne Biometrie am Gerät legt die App das Geheimnis bewusst nicht ab. Der zweite Faktor wäre sonst nur durch den Gerätecode geschützt, und den hat jeder, der das entsperrte Telefon in der Hand hält. Wer keine Biometrie nutzt, verwendet weiter eine separate Authenticator-App. Die App erklärt das beim Einrichten genau so und weist darauf hin, dass der Schlüssel nur ein einziges Mal angezeigt wird.
Face ID statt Passwort beim nächsten Start
Nach der ersten Anmeldung muss niemand bei jedem Öffnen der App sein Passwort tippen. Beim nächsten Start gibt Face ID oder der Fingerabdruck das gespeicherte Erneuerungstoken frei, die App holt still ein neues Zugangstoken, und man ist drin. Die Biometrie lässt sich im Profil abschalten, dann fragt die App bei jedem Start nach dem Passwort. Ist das Erneuerungstoken ungültig, etwa weil das Gerät im Web abgemeldet wurde, landet man auf der Anmeldung, das Token wird gelöscht und ein Ereignis im Protokoll geschrieben.
Fernsteuerung einer Windkraftanlage per App: drei Gates und Biometrie
Jetzt kommt der Teil, bei dem aus einer Monitoring-App ein sicherheitskritisches System wird. Die Plattform kann Windkraftanlagen nicht nur beobachten, sondern nach schriftlicher Freigabe des Auftraggebers auch bedienen, bis vor die Anlage. Die Steuerkonsole schickt dieselben Tastenbefehle, die ein Techniker am Controller im Turm drücken würde. Die ursprüngliche Planung sah die Steuerung nur im Web vor. Ein Handy ist verlierbar, in der Hosentasche entsperrbar und im Funkloch ohne Rückmeldung. Der Auftraggeber hat sich später bewusst für die Steuerung auch in der App entschieden. Wir haben sie gebaut, aber mit zusätzlichen Schranken.
Ein Steuerbefehl erreicht eine Anlage nur, wenn drei unabhängige Bedingungen erfüllt sind. Alle drei prüft ausschließlich der Server. Die App zeigt den Zustand nur an. Wer die Tasten in einer manipulierten App aktiviert, bekommt vom Server trotzdem eine Ablehnung.
Gate | Bedeutung | Wer es setzt |
|---|---|---|
1. Rolle darf steuern | Nur Administrator-Rollen haben das Steuerrecht | Ergibt sich aus der Rolle |
2. Sendeweg ist offen | Ein globaler Hauptschalter auf dem Server, im Standard aus | Plattformbetreiber, einmalig, bewusst nicht im Dashboard |
3. Diese Anlage ist freigegeben | Freigabe je einzelner Anlage, neue Anlagen sind immer gesperrt | Plattform-Administrator im Anlagenregister |
Das dritte Gate haben wir nachträglich eingeführt. Vorher durfte, wer steuern durfte, jede Anlage seines Mandanten steuern. Eine neu angelegte Anlage war sofort mitsteuerbar. Das ist jetzt ausgeschlossen. Jede Freigabe und jede Sperrung steht mit E-Mail und Zeitpunkt im Audit-Protokoll, und das Register zeigt, wer zuletzt freigegeben hat. Eine Sperre wirkt sofort, ohne Neuanmeldung, weil das Gate bei jedem einzelnen Befehl neu geprüft wird.
Der Hauptschalter liegt absichtlich außerhalb der Anwendung. Wer das Dashboard übernimmt, soll damit nicht den Sendeweg zu allen Anlagen öffnen können. Im Standard steht er auf einem Adapter, der jeden Befehl protokolliert, aber nichts sendet. Der echte Adapter verweigert den Start, solange ihm das Passwort für die Box fehlt. Er fällt also sicher aus, nicht offen.
Die Zusatzschranke fürs Handy
Für die App kommt eine vierte Schranke hinzu, die im Web nicht nötig ist. Die Tasten der Steuerkonsole sind erst bedienbar, nachdem man sich auf dieser Seite per Face ID oder Touch ID bestätigt hat, einmal je Aufruf. Ein entsperrtes Handy in der Hosentasche reicht also nicht, um eine Anlage zu schalten. Jeder Tastendruck bekommt außerdem einen eigenen Idempotenzschlüssel. Wird ein Befehl wegen einer wackligen Mobilfunkverbindung doppelt übertragen, führt der Server ihn trotzdem nur einmal aus.
Ehrliche Rückmeldung statt grüner Haken
Auch bei der Steuerung gilt die Regel, nichts vorzutäuschen. Die Konsole zeigt jederzeit, in welchem Zustand sie ist, und zwar in Worten, nicht nur in Farben.
Anzeige | Bedeutung |
|---|---|
Steuerung freigegeben | Befehle erreichen die Anlage |
Probebetrieb, Befehle werden nicht gesendet | Alles offen, aber der sendende Adapter ist aus; es wird nur protokolliert |
Anlage nicht freigegeben | Gate 3 fehlt, im Anlagenregister freigeben |
Steuerung serverseitig deaktiviert | Gate 2 fehlt, Hauptschalter auf dem Server |
Nur Monitoring, keine Steuerberechtigung | Gate 1 fehlt, die Rolle darf nicht steuern |
Im Probebetrieb leuchtet die Anzeige bernsteinfarben, nicht grün. Die Konsole ist bedienbar, aber ohne Wirkung. Grün wäre irreführend. Dieselbe Auskunft liefert der Server auch maschinenlesbar, mit jedem Gate einzeln und einem Klartext-Satz, welches fehlt. Wer schon einmal eine Anlage aus der Ferne bedient hat, weiß, wie viel diese Klarheit wert ist.
Eine Steuerung, bei der man nicht sicher weiß, ob der Befehl angekommen ist, ist keine Steuerung, sondern ein Risiko.
Was die Plattform bewusst nicht kann
Ehrlichkeit gilt auch für die Grenzen. Das Wartungspersonal vor Ort bedient die Anlage am Terminal im Turm. Diese Eingaben kann die Plattform nicht mitschreiben, denn das Terminal ist für sie nur lesbar, und der Zugang zur Box selbst gehört einem Dritten. Die Plattform sieht aber an den Werten, wenn jemand vor Ort durch das Menü blättert, und sie protokolliert, wer sich zur Wartung an- und abgemeldet hat. Wir haben diese Grenze im Projekt früh und schriftlich benannt, statt eine Nachverfolgung zu versprechen, die technisch nicht möglich ist.
Mandantentrennung: 404 statt 403
Auf der Plattform arbeiten mehrere Kunden, und jeder darf nur seine eigenen Anlagen sehen. Die Trennung zieht sich durch jede Tabelle: Ein Messwert gehört zu einer Anlage, die Anlage zu einem Windpark, der Windpark zu einem Mandanten. Jede Anfrage wird auf den Mandanten des Nutzers beschränkt, bevor überhaupt eine Zeile gelesen wird.
Ein Detail zeigt, wie ernst das gemeint ist. Fragt jemand eine Anlage eines fremden Mandanten ab, antwortet der Server mit 404 „nicht gefunden“ und nicht mit 403 „verboten“. Ein 403 würde verraten, dass es diese Anlage gibt. Ein 404 verrät nichts. Dazu kommen vier Rollen: Plattform-Administrator, Mandanten-Administrator, Mitarbeiter und Betreiber mit reinem Sicht-Recht. Einzelne Anlagen lassen sich über Freigaben für andere Mandanten sichtbar machen, ohne dass sie den Besitzer wechseln. So kann ein Betreiber eine Anlage sehen, die einem Dienstleister gehört, und umgekehrt.
Push-Benachrichtigungen ohne Messwerte auf dem Sperrbildschirm
Die Push-Zustellung für Alarme ist im Backend vollständig gebaut. Jedes Gerät, das Nachrichten empfangen soll, wird mit Plattform, Gerätename und Zeitpunkten gespeichert, gebunden an Nutzer und Mandant. Nutzer sehen ihre Geräte in App und Web und können jedes einzeln abmelden. Administratoren können Geräte eines Nutzers widerrufen. Ausgelöst wird eine Nachricht genau dort, wo auch heute ein Alarm entsteht.
Wenig Inhalt: Die Nachricht enthält nur Anlage, Schweregrad und Zeit. Messwerte stehen nicht auf dem Sperrbildschirm. Details lädt die App erst nach dem Öffnen und nach der Anmeldung.
Kein zusätzlicher Dienstleister: Push läuft zwangsläufig über Apple und Google. Wir empfehlen, direkt mit diesen beiden Diensten zu sprechen, ohne einen weiteren Vermittler, der Geräte-Token und Inhalte sieht.
Zuverlässig: Versand mit Wiederholung, Backoff und Jitter, Fehler isoliert je Gerät. Ungültige Geräte werden markiert und nicht endlos weiter beschickt.
Datenschutz: Geräte-Token sind Personendaten. Ihre Aufbewahrung folgt denselben konfigurierbaren Regeln wie alles andere und endet mit dem Nutzerkonto.
Hosting und Netz: selbst betrieben in Deutschland
Die gesamte Plattform läuft auf einem Server bei Hetzner in Deutschland. Datenbank, Schnittstelle, Datenerfassung, Web-Dashboard und Reverse Proxy laufen als Container mit Docker Compose hinter nginx. Es gibt keine Cloud-Datenbank, keinen fremden Authentifizierungsdienst und kein Tracking. Das war eine Vorgabe des Auftraggebers und passt zur Branche: Betriebsdaten von Energieanlagen gehören auf Server, deren Standort man kennt. Wie wir solche Umgebungen aufsetzen und betreuen, beschreiben wir auf der Seite Wartung und Support.
Die Windparks sind über Mobilfunk-Router und verschlüsselte Tunnel mit dem Server verbunden. Der Weg geht über WireGuard, ein modernes und schlankes VPN-Protokoll. Aus dem Tunnel kommend darf ein Windpark nur die Schnittstelle der Datenerfassung erreichen, alles andere wird verworfen. Ein kompromittierter Router in einem Windpark kann also nicht in den Rest des Servers.
Neue Standorte ohne Kommandozeile anbinden
Bisher wird ein neuer Standort über ein Skript auf dem Server angebunden. Geplant und spezifiziert ist, dass der Kunde das selbst im Dashboard erledigt. Der Router erzeugt sein Schlüsselpaar selbst, und nur der öffentliche Teil wird ins Dashboard kopiert. Der private Schlüssel verlässt das Gerät nie. Ein Dienst auf dem Server trägt den neuen Tunnel ein, nachdem er die Angaben selbst geprüft hat. Er vertraut der Web-Anwendung dabei ausdrücklich nicht.
Warum so umständlich, wenn man den Container der Anwendung einfach die VPN-Konfiguration schreiben lassen könnte? Weil der Web-Container der exponierteste Teil des Systems ist. Wer ihn übernimmt, hätte dann die Hoheit über die Tunnel zu allen Windparks. Mit der gewählten Lösung kann ein kompromittiertes Backend höchstens neue Tunnel beantragen, aber weder die Serverkonfiguration umschreiben noch Befehle auf dem Server ausführen. Ein Knopf „Verbindung prüfen“ testet danach in vier Stufen, ob der Standort wirklich erreichbar ist.
Der Router erzeugt sein Schlüsselpaar und zeigt den öffentlichen Schlüssel an.
Der Kunde trägt Standortname, öffentlichen Schlüssel und das Netz hinter dem Router im Dashboard ein.
Der Server legt den Tunnel an und zeigt die Werte, die in den Router gehören.
Der Kunde trägt diese Werte im Router ein.
„Verbindung prüfen“ bestätigt Stufe für Stufe, dass der Tunnel steht.
Erst dann wird die erste Anlage angelegt und dem Router zugeordnet.
Qualitätssicherung: fertig ist, was mit echten Daten läuft
In vielen Projekten gilt eine Aufgabe als fertig, wenn der Code geschrieben ist und die Tests grün sind. Bei Windhunde reicht das nicht. Die Definition of Done ist ein Verifikations-Loop mit echten Daten. Er ist in den Projektregeln festgeschrieben und gilt für jedes Arbeitspaket.
Den ganzen Stack wirklich starten: Datenbank, Schnittstelle, Datenerfassung und Oberfläche, verbunden mit echten Anlagen.
Alle Logs beobachten: Server, Datenerfassung, Browser-Konsole beziehungsweise App-Konsole und Netzwerkverkehr.
Jeden Fehler und jede Warnung beheben: Warnungen zählen wie Fehler. Nichts wird unterdrückt, nur um die Ausgabe sauber zu bekommen.
Wiederholen: so lange, bis der Stack mehrere Minuten ohne jede Auffälligkeit läuft.
Erst dann einchecken: mit einem Log-Auszug ohne Fehler und einem Screenshot als Beleg.
Dazu kommen die klassischen Prüfungen. Alle Backend-Tests müssen grün sein. Der handgeschriebene Python-Code muss in der statischen Analyse mit pylint die volle Punktzahl von 10,00 erreichen. Der Build des Web-Dashboards muss ohne Warnung durchlaufen. Und jede Datenbank-Migration muss sich hoch, zurück und wieder hoch spielen lassen. Diese Strenge klingt aufwendig, spart aber am Ende Zeit. Ein Fehler, den man im Verifikations-Loop sieht, kostet Minuten. Derselbe Fehler im Betrieb, bei dem eine Anlage unbemerkt ausfällt, kostet viel mehr.
Belege aus dem Projekt
Prüfung | Ergebnis |
|---|---|
Backend-Tests nach dem Umbau für die App | 336 grün, davon 304 bestehende Tests unverändert |
Tests des geteilten Pakets | 31 grün, ausgeführt unter Node ohne Browser-Umgebung |
Statische Analyse des Python-Codes | pylint 10,00 von 10 |
Typprüfung des geteilten Pakets ohne Browser-Bibliothek | sauber, kein versehentlicher Browser-Zugriff |
Web-Build | sauber, 16 Routen inklusive neuem Impressum |
Datenbank-Migrationen | hoch, zurück und wieder hoch reproduzierbar |
Erster Build der App auf dem Simulator | Build erfolgreich, 0 Fehler, 0 Warnungen |
Für die App gelten zusätzliche Kriterien, die nur auf echten Geräten prüfbar sind. Live-Werte in App und Web müssen zur selben Sekunde identisch sein, belegt durch zwei Screenshots nebeneinander. Nach dem Wechsel aus dem Hintergrund muss die App einen neuen Schnappschuss holen. Nach Flugmodus an und aus muss sie sich ohne Neustart wieder verbinden. Und sie muss 30 Minuten ohne Fehler im Server-Log und in der App-Konsole laufen. Genau diese Prüfungen haben die Leiste für den Verbindungszustand und das saubere Schließen im Hintergrund hervorgebracht.
Tests, die echte Displays verwenden
Besonders wichtig sind die Tests des Parsers. Sie arbeiten mit echten Display-Inhalten aus den Anlagen, inklusive Latin-1-Zeichen, deutscher und englischer Beschriftung, beider Firmware-Generationen und Menüzeilen. Jeder Fehler, den wir im Betrieb gefunden haben, wurde zuerst als Test mit dem echten Display nachgestellt und erst dann behoben. So kann derselbe Fehler nicht unbemerkt zurückkommen. Für ein System, das Werte aus Text liest, ist das die wirksamste Versicherung überhaupt.
Projektstand im September 2026
Die App wurde in fünf Arbeitspaketen geplant. Jedes hat eine eigene Definition of Done, und der Stand ist nach diesen Kriterien bewertet, nicht nach Gefühl.
Arbeitspaket | Inhalt | Stand |
|---|---|---|
Fundament | Geteiltes Paket, Token-Anmeldung, Impressum als Daten, Geräteliste | Erledigt |
Kern | App-Gerüst, Anmeldung mit Zwei-Faktor und Biometrie, Dashboard live, Anlagenstatus | Erledigt |
Alarm | Geräteverwaltung, Push-Versand, Alarmzentrale, Direktlinks | Backend erledigt, Push wartet auf die Zugangsdaten von Apple und Google |
Verwaltung | Anlagen-Detail, Terminal, Protokoll, Register, Wartung, Nutzer | Teilweise erledigt; Wartungs-Anmeldung, Einsätze und Nutzerverwaltung folgen |
Store | Icons, Store-Screenshots, Datenschutzangaben, Review-Zugang, Einreichung | In Vorbereitung, Einreichung über TestFlight und Play Internal Testing |
Für die Prüfung durch Apple und Google ist ein eigener Review-Zugang geplant: ein Demo-Mandant, der echte Anlagen nur lesend sieht. Gefälschte Werte für die Prüfer kommen nicht in Frage. Das wäre gegen die wichtigste Regel des Projekts, und es würde im Review auch auffallen. Die Screenshots in dieser Case Study stammen aus genau so einem Demo-Zugang.
Was als Nächstes kommt
Wartungs-Anmeldung in der App: Kamera-Scan der Anlagennummer, Offline-Entwurf, der ein Beenden der App übersteht, und sichtbare Bestätigung nach dem Absenden.
Push-Nachrichten live: Freischaltung, sobald die Store-Konten und die Schlüssel für die Push-Dienste bereitstehen.
Nutzerverwaltung und Vorschau als Nutzer: damit Administratoren auch unterwegs Konten anlegen und prüfen können, was ein Nutzer sieht.
Eigenes Dashboard je Kunde: eigene Adresse, eigenes Logo und Selbstregistrierung neuer Kunden mit Freigabe durch einen Plattform-Administrator.
Push vom Router: gebündelte Werte statt Einzelabfragen über Mobilfunk, bei voller Auflösung und rund 96 Prozent weniger Datenvolumen.
Standorte per Klick anbinden: Router-Anbindung im Dashboard mit automatischer Verbindungsprüfung.
Was wir aus dem Projekt mitnehmen
Jedes Projekt verändert die Art, wie wir arbeiten. Aus der Windhunde-App nehmen wir sieben Lehren mit, die für jede Monitoring-App und für viele andere Apps gelten.
1. Erst das Web, dann die App
Die App kam, als das Web-Dashboard mit echten Anlagen stabil lief. Dadurch gab es keine offenen Fragen dazu, was angezeigt werden soll. Die App-Entwicklung konnte sich ganz auf das konzentrieren, was auf dem Handy anders ist: Navigation, Vordergrund und Hintergrund, Biometrie und Offline-Verhalten. Wer beides gleichzeitig baut, klärt dieselben Fachfragen zweimal.
2. Logik teilen, Oberfläche nicht
Das geteilte Paket war die wichtigste Investition des Projekts. Es hat verhindert, dass Web und App je unterschiedliche Werte zeigen. Die Oberfläche dagegen haben wir bewusst nicht geteilt. Ein Handy braucht Karten statt Tabellen, Tabs statt Seitenleiste und Gesten statt Mauszeiger. Code, der beides gleichzeitig können soll, ist am Ende auf beiden Plattformen schlecht.
3. Alte Datenquellen verdienen die meiste Aufmerksamkeit
Die meisten Stunden flossen nicht in die schönen Screens, sondern in den Parser für ein vierzeiliges Display. Zeichensatz, Sprache, Firmware und Menüzustand haben jeweils still falsche Werte erzeugt, bis wir sie abgefangen hatten. Wer ein Altsystem anbindet, sollte dort anfangen und dort die meisten Tests schreiben.
4. Datensparen erst rechnen, dann bauen
Die Annahme, Conditional GET würde das Mobilfunkvolumen drastisch senken, war falsch. Die Fixkosten jeder Abfrage waren größer als die Nutzlast. Erst die Rechnung hat gezeigt, dass die echte Ersparnis im Router liegt. Ohne diese Rechnung hätten wir an der falschen Stelle optimiert.
5. Ehrliche Anzeigen sind ein Sicherheitsmerkmal
Ein eingefrorener Wert, der wie ein aktueller aussieht, ist im Monitoring gefährlicher als eine leere Anzeige. Live-Leiste, Kontaktalter, Lücken im Diagramm, bernsteinfarbener Probebetrieb und ein Impressum, das fehlende Angaben zugibt, folgen alle derselben Idee. Nutzer sollen der App vertrauen können, weil sie nie mehr behauptet, als sie weiß.
6. Jede Bequemlichkeit braucht eine Schranke
Face ID statt Passwort, die App als zweiter Faktor und Fernsteuerung vom Handy sind bequem. Jede dieser Funktionen haben wir nur mit einer passenden Schranke gebaut: Biometrie vor dem gespeicherten Zugang, kein zweiter Faktor ohne Biometrie, drei Gates auf dem Server plus Biometrie je Aufruf vor der Steuerung. Bequemlichkeit und Sicherheit schließen sich nicht aus, wenn man sie zusammen plant.
7. Lücken benennen statt verstecken
Die Wartungs-Anmeldung führt in der App vorerst auf die Webseite. Die Nutzerverwaltung fehlt im Menü, statt auf einen leeren Screen zu führen. Das Protokoll sagt, wenn es nur tausend Werte zeigt. Solche offenen Stellen sind normal. Problematisch werden sie erst, wenn eine App so tut, als gäbe es sie nicht.
Glossar: Begriffe aus Windkraft und App-Entwicklung
In dieser Case Study kommen Begriffe aus zwei Welten zusammen, der Windenergie und der Software-Entwicklung. Hier sind die wichtigsten kurz erklärt.
Begriff | Erklärung |
|---|---|
Windkraftanlage, WEA | Anlage, die aus Wind elektrische Energie erzeugt; im Fachjargon Windenergieanlage, abgekürzt WEA |
Leistung in kW | Momentan erzeugte elektrische Leistung; negative Werte bedeuten Eigenverbrauch der Anlage, etwa bei Windstille |
Rotordrehzahl | Umdrehungen des Rotors mit den Rotorblättern pro Minute, typisch im niedrigen zweistelligen Bereich |
Generatordrehzahl | Umdrehungen des Generators pro Minute; durch das Getriebe um ein Vielfaches höher als die Rotordrehzahl |
Pitch | Anstellwinkel der Rotorblätter; über ihn regelt die Anlage Leistung und bremst bei Sturm oder Stopp |
Azimut | Ausrichtung der Gondel nach der Windrichtung; ein Azimut-Stopp hält diese Nachführung an |
Controller, Steuerbox | Rechner im Turm, der die Anlage steuert und ihre Werte auf einem Display anzeigt |
SCADA | Supervisory Control and Data Acquisition, Oberbegriff für Systeme zur Überwachung und Steuerung technischer Anlagen |
Poller | Dienst, der Datenquellen in festen Abständen abfragt, hier alle Anlagen alle zwei Sekunden |
Conditional GET | HTTP-Abfrage mit Kennung der letzten Antwort; bei unveränderten Daten antwortet der Server nur mit 304 ohne Inhalt |
Zeitreihen-Datenbank | Datenbank, die auf Daten mit Zeitstempel und Abfragen über Zeiträume optimiert ist, hier PostgreSQL mit TimescaleDB |
Server-Sent Events, SSE | Dauerhaft offene Verbindung, über die der Server Neuigkeiten an Web oder App schickt |
Snapshot und Delta | Vollständiger Stand beim Verbinden, danach nur die Änderungen |
Mandant | Kunde auf der Plattform mit eigenen, von anderen getrennten Daten und Nutzern |
Gate | Bedingung, die erfüllt sein muss, bevor eine Aktion ausgeführt wird, hier für Steuerbefehle |
Idempotenz | Eigenschaft, dass eine doppelt ausgeführte Aktion dasselbe Ergebnis hat wie eine einfache |
TOTP | Zeitbasiertes Einmalpasswort, der sechsstellige Code einer Authenticator-App für die Zwei-Faktor-Anmeldung |
WireGuard | Modernes VPN-Protokoll, über das die Windparks verschlüsselt mit dem Server verbunden sind |
Expo | Werkzeugkasten für React Native mit gemeinsamem Build, Store-Einreichung und nativen Modulen |
Web-Dashboard und App im Zusammenspiel
Web und App sind keine Konkurrenten, sondern zwei Werkzeuge für unterschiedliche Situationen. Das Web-Dashboard ist der Arbeitsplatz in der Leitstelle oder im Büro. Dort stehen große Tabellen, der Export des Protokolls als CSV oder PDF, die Nutzerverwaltung und die Einstellungen für Freigaben und Anbindungen. Die App ist das Werkzeug für unterwegs. Sie zeigt auf einen Blick, ob alles läuft, meldet Alarme und erlaubt einen schnellen Blick auf eine einzelne Anlage.
Weil beide auf dieselbe Schnittstelle zugreifen, greifen sie nahtlos ineinander. Ein Layout, das jemand auf dem Handy umsortiert, erscheint genauso im Web. Ein Gerät, das im Web abgemeldet wird, verliert in der App sofort den Zugang. Eine Anlage, die ein Administrator im Web zur Steuerung freigibt, ist in der App mit derselben Freigabe zu sehen. Und ein Alarm, der auf dem Handy auffällt, lässt sich am Rechner im Protokoll mit allen Werten des Zeitraums nachverfolgen. Genau diese Arbeitsteilung war das Ziel: nicht jede Funktion überall, sondern jede Funktion dort, wo sie am besten zu bedienen ist.
Für wen eignet sich eine solche Monitoring-App?
Die Windhunde-App ist für Windkraft gebaut, aber das Muster dahinter passt auf viele Branchen. Überall dort, wo Anlagen über ältere Steuerungen Daten liefern, aber niemand sie bequem und sicher unterwegs sehen kann, hilft derselbe Aufbau aus Datenerfassung, Zeitreihen-Datenbank, Live-Stream und nativer App.
Windpark-Betreiber und Betriebsführer, die viele Anlagen verschiedener Baureihen in einer Oberfläche sehen wollen.
Service- und Wartungsdienstleister, die Einsätze vor Ort dokumentieren und Störungen schneller erkennen wollen.
Betreiber von Photovoltaik, Blockheizkraftwerken oder Pumpstationen mit ähnlichen Steuerungen und Mobilfunk-Anbindung.
Industriebetriebe, die Maschinen mit alten Controllern ohne Austausch der Steuerung ins Monitoring holen wollen.
Anbieter, die daraus ein Produkt machen wollen, mit eigenem Dashboard je Kunde und White-Label-App.
Wenn du ein ähnliches Vorhaben hast, sprechen wir gern darüber. Wir bauen Mobile Apps mit React Native und Expo, Web-Applikationen und Dashboards mit Next.js sowie Schnittstellen und Datenerfassung in Python mit FastAPI. Hosting in Deutschland und Betreuung nach dem Launch gehören dazu. Eine weitere App-Case-Study aus einer ganz anderen Branche findest du bei myNextDays. Und wenn du direkt loslegen willst, erreichst du uns über das Kontaktformular.
Häufige Fragen zur Windpark-Monitoring-App
Was ist eine Windpark-Monitoring-App?
Eine Windpark-Monitoring-App zeigt Betreibern und Technikern auf dem Smartphone, wie es ihren Windkraftanlagen gerade geht. Dazu gehören Live-Werte wie Leistung, Windgeschwindigkeit, Drehzahlen und Pitch-Winkel, der Zustand jeder Anlage, Alarme und der Verlauf der letzten Stunden oder Tage. Bei der Windhunde-App kommen die Werte aus einer Datenerfassung, die jede Anlage alle zwei Sekunden abfragt und jede Änderung in einer Zeitreihen-Datenbank speichert. Die App greift auf dieselbe Schnittstelle zu wie das Web-Dashboard und zeigt deshalb exakt dieselben Werte.
Kann man ältere Windkraftanlagen ohne neue Steuerung überwachen?
Ja, in vielen Fällen. Auch ältere Anlagen haben meist eine Steuerbox, die ihre Werte über ein Netzwerk herausgibt, oft nur als Text oder als kleine XML-Datei. Diese Daten lassen sich regelmäßig abfragen, auswerten und speichern, ohne die Steuerung zu tauschen oder in die Anlage einzugreifen. Bei Windhunde spiegelt die Box ihr vierzeiliges Display. Ein Parser liest daraus die Messwerte, erkennt verschiedene Firmware-Generationen und Sprachen und verwirft Zahlen, die aus dem Menü statt aus der Übersicht stammen. Wichtig ist, dass die Box selbst keine Historie hält. Die Vergangenheit existiert nur, wenn laufend abgefragt und gespeichert wird.
Wie viel Datenvolumen braucht die Überwachung einer Windkraftanlage über Mobilfunk?
Das hängt vor allem vom Abfrageintervall und vom Übertragungsweg ab, weniger von der Datenmenge selbst. Bei Windhunde ist eine Abfrage nur rund 600 Byte groß, aber Kopfzeilen und VPN-Overhead kosten bei jeder Abfrage zusätzlich fast 1.000 Byte. Bei 13 Anlagen und einer Abfrage alle zwei Sekunden ergibt das rechnerisch etwa 17 bis 26 GB im Monat. Deutlich sparsamer ist es, wenn der Router im Windpark die Anlagen lokal abfragt und die Werte gebündelt einmal pro Minute an den Server schickt. Dann sinkt das Volumen auf unter 1 GB im Monat, bei voller Auflösung.
Warum React Native und nicht Flutter oder zwei native Apps?
Weil das Web-Dashboard bereits in TypeScript und React geschrieben war. Mit React Native konnten wir die gesamte Rechen- und Anzeigelogik als gemeinsames TypeScript-Paket für Web und App nutzen, statt sie in Dart oder in Swift und Kotlin neu zu schreiben. Das spart Entwicklungszeit und verhindert, dass zwei Umsetzungen derselben Formel auseinanderlaufen. Expo liefert zusätzlich einen gemeinsamen Build- und Einreichungsweg für App Store und Google Play sowie fertige Module für sichere Ablage, Biometrie, Kamera und Push. Die App ist trotzdem eine echte native App ohne WebView.
Wie kommen die Live-Werte in Echtzeit auf das Handy?
Über Server-Sent Events, also eine dauerhaft offene Verbindung, über die der Server Neuigkeiten schickt. Nach jedem neuen Messwert sendet die Datenbank per LISTEN und NOTIFY ein Signal, und der Stream-Dienst leitet die Änderung an alle verbundenen Geräte des Mandanten weiter. Beim Verbinden bekommt die App zuerst einen vollständigen Schnappschuss, danach nur noch die Änderungen. Geht die App in den Hintergrund, schließt sie die Verbindung. Kommt sie zurück, holt sie einen neuen Schnappschuss. Eine Leiste zeigt jederzeit, wie aktuell die Werte sind.
Ist es sicher, eine Windkraftanlage per App fernzusteuern?
Nur mit mehreren unabhängigen Schranken. Bei der Windhunde-App erreicht ein Steuerbefehl eine Anlage nur, wenn die Rolle des Nutzers steuern darf, ein Hauptschalter auf dem Server den Sendeweg geöffnet hat und genau diese Anlage freigegeben ist. Alle drei Bedingungen prüft ausschließlich der Server. Neue Anlagen sind immer gesperrt, und jede Freigabe steht im Audit-Protokoll. Zusätzlich sind die Tasten in der App erst nach Face ID oder Touch ID bedienbar, einmal je Aufruf der Seite. Jeder Tastendruck hat einen eigenen Idempotenzschlüssel, damit ein doppelt übertragener Befehl nur einmal ausgeführt wird.
Wo liegen die Daten der Windhunde-Plattform?
Auf einem eigenen Server bei Hetzner in Deutschland. Datenbank, Schnittstelle, Datenerfassung und Web-Dashboard laufen selbst betrieben als Container, ohne Cloud-Datenbank, ohne fremden Anmeldedienst und ohne Tracking. Die Windparks sind über Mobilfunk-Router und verschlüsselte WireGuard-Tunnel angebunden. Aus einem Tunnel kommend erreicht ein Windpark nur die Datenerfassung, nicht den Rest des Servers. Aufbewahrungsfristen stehen in einer eigenen Tabelle und lassen sich ohne Update ändern, Personendaten sind von technischen Daten getrennt.
Können mehrere Kunden dieselbe Plattform nutzen?
Ja. Die Plattform ist mandantenfähig. Jeder Messwert gehört über Anlage und Windpark zu genau einem Mandanten, und jede Anfrage wird auf den Mandanten des Nutzers beschränkt. Fragt jemand eine fremde Anlage ab, antwortet der Server mit nicht gefunden, damit nicht einmal ihre Existenz verraten wird. Es gibt vier Rollen vom Plattform-Administrator bis zum Betreiber mit reinem Sicht-Recht. Einzelne Anlagen lassen sich für andere Mandanten sichtbar machen. Geplant sind außerdem eigene Dashboards je Kunde mit eigener Adresse, eigenem Logo und Selbstregistrierung.
Funktioniert die App auch ohne Netz im Windpark?
Live-Werte brauchen eine Verbindung, denn sie kommen direkt vom Server. Ohne Netz zeigt die App deshalb keinen alten Wert als aktuell an, sondern macht sichtbar, dass die Verbindung fehlt und wie alt die letzten Daten sind. Bei einem Netzwechsel zwischen WLAN und Mobilfunk verbindet sie sich sofort neu. Für die Wartungs-Anmeldung vor Ort ist ein Offline-Entwurf geplant, der gespeichert wird und auch ein Beenden der App übersteht. Abgeschickt wird er, sobald wieder Netz da ist, mit einer sichtbaren Bestätigung.
Was kostet die Entwicklung einer Monitoring-App für Windkraftanlagen?
Das hängt davon ab, was schon da ist. Existieren bereits eine saubere Datenerfassung, eine Schnittstelle und ein Web-Dashboard, ist die App vor allem Oberflächenarbeit und entsprechend schneller umgesetzt. Muss die Datenquelle erst erschlossen werden, etwa bei älteren Anlagen mit Textdisplays, entfällt der größte Aufwand auf Parser, Datenerfassung und Tests. Weitere Kostentreiber sind Fernsteuerung mit Sicherheitsschranken, Mandantenfähigkeit, Push-Nachrichten und die Einreichung in beiden Stores. Wir erstellen nach einem kurzen Gespräch eine Aufwandsschätzung auf Basis deiner Anlagen und Anforderungen.
- React Native 0.86 · Expo SDK 57Native App für iOS und Android, ohne WebView
- Expo RouterDateibasierte Navigation, strukturgleich zum Web
- NativeWindDesign-Tokens aus dem Web-Dashboard, Dunkelmodus
- react-native-sseLive-Stream mit Token im Header
- FastAPIGemeinsame Schnittstelle für Web und App
- PostgreSQL · TimescaleDBZeitreihen-Datenbank, self-hosted
- Next.js 16Web-Dashboard mit Drag-and-Drop-Layout
- WireGuardVerschlüsselte Tunnel zu den Windparks



Du willst deine Anlagen aufs Smartphone bringen?
Kontakt aufnehmenDatenerfassung aus bestehenden Steuerungen, Zeitreihen-Datenbank, Live-Dashboard und native App mit sicherer Fernsteuerung – genau das bauen wir auch für dich. Self-hosted in Deutschland, vom ersten Messwert bis zum Store-Release.