Eine Android / iOS App für Gladys: Ansatz und erste Ergebnisse

Hallo zusammen,

Nach dem Live-Stream von gestern und der Anforderung einer mobilen Anwendung habe ich Claude einfach mal auf das Thema angesetzt, nur um zu sehen. In weniger als 15 Minuten hat er eine vollständige Spezifikation und eine Demo auf meinem Android-Telefon erstellt, auf der ich meine Dashboards, Szenen… alles von Gladys wiederfinde. Ich bin sprachlos… :star_struck:

Hier ist seine Analyse:
Das „ich“ in der Nachricht ist Claude, ich hätte das in 15 Minuten nicht schaffen können.

Ich habe mit der Arbeit an einer nativen mobilen Anwendung für Gladys begonnen und möchte den Ansatz teilen, bevor ich weitergehe — insbesondere die Architekturwahl, die mir der diskutabelste Punkt zu sein scheint und daher der, bei dem euer Feedback am nützlichsten wäre.

Die Basis funktioniert: Die App läuft auf meinem Telefon, verbunden mit meiner Instanz, mit allen meinen Dashboards und Kameras.


Das Problem

Heute ist Gladys auf mobilen Geräten entweder der Browser oder das Hinzufügen zum Startbildschirm. Das funktioniert, aber es hat Grenzen:

  • keine zuverlässigen Benachrichtigungen;
  • kein Hintergrund-Präsenz-Tracking;
  • keine Präsenz in den Stores;
  • und der aktuelle Service Worker (front/old-sw.js) deinstalliert sich absichtlich bei jeder Aktivierung — es gibt also keine nutzbare PWA-Basis, auf der man aufbauen könnte.

Die Architekturwahl: Integration des bestehenden Frontends

Das ist die entscheidende Entscheidung. Drei Optionen standen zur Auswahl.

Option A — React Native, eine separate native App

Eine neu geschriebene App, die die REST-API und das WebSocket von Gladys nutzt. Bessere Leistung, echtes natives Gefühl.

Aber man muss die gesamte Oberfläche neu schreiben: 23 Routen, 22 Widget-Typen für Dashboards, den Szenen-Editor, die Karte, den Chat, die Integrationsseiten. Und vor allem muss man zwei Oberflächen parallel pflegen: Jede Änderung der Web-Oberfläche müsste manuell in die mobile App übertragen werden, mit dem Risiko von Divergenzen, das damit einhergeht. Für ein Community-Projekt erscheint mir das auf Dauer nicht tragbar.

Option B — PWA verbessern

Service Worker reparieren, Offline-Cache und Web Push hinzufügen. Leicht, kein Store-Management nötig.

Aber das löst weder die Hintergrund-Geolokalisierung, noch die Startbildschirm-Widgets, noch die Präsenz in den Stores. Und auf iOS bleibt Web Push eingeschränkt. Man würde mit denselben Grenzen bleiben.

Option C — Capacitor + bestehendes Frontend ← ausgewählt

Das aktuelle Gladys-Frontend (Preact / Vite) wird genau wie heute kompiliert und dann in einer nativen WebView durch Capacitor eingebettet. Die Fähigkeiten, die das Web nicht bieten kann, werden über Plugins hinter einer Abstraktionsschicht bereitgestellt.

front/ (Preact, unverändert)
   └── build/ ──> Capacitor WebView
                   ├── android/
                   └── ios/

+ native Plugins: Push, Geolokation, Biometrie,
                   mDNS, sicherer Speicher, Widgets

Warum diese Wahl:

  • Ein einzelner Codebase. Jede Änderung des Frontends profitiert dem Web, Android und iOS ohne Portierung. Das ist der entscheidende Punkt für ein Community-Projekt.
  • Fast vollständige Wiederverwendung. Bildschirme, Widgets, Aktionen, Übersetzungen, dunkles Thema: Alles wird unverändert übernommen.
  • Nichts verloren auf der nativen Seite. Push-Benachrichtigungen, Geolokalisierung durch Zonen, Biometrie, mDNS-Entdeckung, Startbildschirm-Widgets bleiben über die Plugins zugänglich.
  • Umkehrbar. Falls der Ansatz seine Grenzen zeigt, bleibt das Frontend das Frontend — es wurde keine parallele Schuldenlast geschaffen.

Das Leitprinzip: Die native Hülle enthält keine Geschäftslogik. Sie stellt Fähigkeiten bereit, das Frontend nutzt sie. Jede native Funktionalität ist optional, und ihr Fehlen (abgelehntes Berechtigung, nicht kompatible Plattform) bricht die Anwendung nie.


Was bereits umgesetzt wurde: Die Verbindungsgrundlage

Der erste Teil beantwortet eine Frage, die das Web-Frontend nie stellt: mit welcher Instanz soll kommuniziert werden?

Im Web ist die Antwort trivial — das Frontend wird von der Instanz selbst bereitgestellt. Eine mobile App hingegen wird in einem einzigen Build für alle verteilt und muss vom Benutzer konfiguriert werden.

Konkreter:

  • Konfiguration zur Laufzeit. front/src/config.js las process.env, das Vite durch Literale beim Build ersetzt. Das exportierte Objekt ist nun veränderbar, mit einer weißen Liste: Nur die Verbindungsschlüssel können überschrieben werden, damit ein fehlerhaft gespeichertes Profil den Demo-Modus nicht aktivieren kann.
  • Erstverbindungsbildschirm, der die Adresse der Instanz anfordert, mit Adresstest vor der Speicherung.
  • Multi-Instanz-Profile (Hauptwohnsitz, Nebenwohnsitz, Testinstanz), gespeichert im Keychain / Keystore.
  • Automatischer Wechsel zwischen lokalem Netzwerk und Gladys Plus, mit einer Sonde auf /api/v1/ping.
  • Einstellungenseite zum Verwalten dieser Instanzen — auf dem Web verborgen, wo sie keinen Sinn ergeben würde.

Auswirkung auf das Web-Frontend: null. Der native Code wird vollständig aus dem Web-Bundle durch Tree-Shaking entfernt, und die Build-Werte bleiben dieselben. Manuell überprüft im Browser.


Drei Dinge, die die Spezifikation nicht vorhergesehen hat

Das ist der lehrreichste Teil und der, der anderen helfen kann.

1. Die Mixed-Content-Regel und der Kompromiss, den sie erfordert

Damit die Ende-zu-Ende-Verschlüsselung von Gladys Plus funktioniert, wird crypto.subtle benötigt, das nur in einem sicheren Kontext verfügbar ist. Capacitor erhält dies, indem es die WebView über https://localhost bereitstellt.

Allerdings kann eine Seite https:// weder http:// noch ws:// aufrufen — Chromium blockiert dies. Eine lokale Gladys-Instanz ist jedoch sehr oft in einfachem HTTP. Die Netzwerksicherheitseinstellungen von Android ändern daran nichts: Es ist eine Richtlinie des Browsers, nicht der Plattform.

Das Plugin CapacitorHttp löst das Problem der HTTP-Anfragen (sie verlassen die native Schicht), aber nicht das der WebSockets, die weiterhin blockiert werden. Ohne WebSocket keine Echtzeit.

Daher die Abwägung, die man kennen muss:

androidScheme Sicherer Kontext Gladys Plus Lokales HTTP WebSocket
https ja OK über CapacitorHttp blockiert
http nein nicht verfügbar OK OK

Für diesen ersten Teil habe ich http gewählt: Das Ziel war die Verbindung zur lokalen Instanz, die das WebSocket erfordert. Es ist explizit temporär. Die saubere Lösung ist, die Instanz über einen Reverse-Proxy (Caddy, nginx, Traefik) in HTTPS zu servieren: Alles läuft dann über https/wss, keine Blockaden mehr, sicherer Kontext erhalten. Das ist auch der einzige Weg, der für iOS gelten wird.

Wenn ihr dazu eine Meinung habt, interessiert mich das sehr: Sollte man annehmen, dass eine Instanz in HTTPS für die Nutzung von Gladys Plus aus der mobilen App erforderlich ist, oder ist es besser, in ein natives WebSocket-Plugin zu investieren, um bei https zu bleiben?

2. Ein HTTP-Client, der seine URL einfror

HttpClient fing config.localApiUrl in seinem Konstruktor ein — ausgeführt beim Laden des Moduls, also bevor das mobile Profil aufgelöst wurde. In der WebView ist window.location.origin gleich https://localhost, also die App selbst: Alle Anfragen wären ins Leere gegangen.

Es ist jetzt ein Getter, der die Konfiguration bei jeder Anfrage liest. Im Web ändert sich der Wert nie, also identisches Verhalten.

3. Fehlendes viewport-fit=cover

Die obere Leiste der App ging unter der Android-Uhr und den Benachrichtigungssymbolen verloren. Die Ursache war nicht der Header, sondern das <meta viewport>: seiMit viewport-fit=cover sind alle Variablen env(safe-area-inset-*) gleich null.

Interessanter Punkt: Die Frontend-Gladys verwendet bereits safe-area-inset-bottom an fünf Stellen (Chat, Dashboard, Geräteliste). Sie hatten also auf mobilen Geräten keine Wirkung. Der Fix aktiviert sie alle.


Validierung auf Gerät

Getestet auf einem Xiaomi unter Android 16 (WebView Chromium 151) gegen eine echte Instanz:

Punkt Ergebnis
Web Crypto (RSA-OAEP 2048, ECDSA P-256, AES-GCM, PBKDF2) 9/9 im https-Kontext
WebSocket zu einer HTTP-Instanz Handshake in 44 ms
MSE / hls.js (Kameras) Verfügbar
Vollständiger Ablauf: Konfiguration, Login, Dashboards, Kameras Funktionell

Eine gegenintuitive Messung: PBKDF2 100.000 Iterationen in 15 ms auf dem Telefon, gegenüber 45 ms auf meinem PC. Die Android-Implementierung ist hardwarebeschleunigt. Das von mir antizipierte Risiko einer langsamen Entsperrung auf mobilen Geräten existiert nicht.

Eine weitere Lehre: Mein anfänglicher Validierungsprototype gab drei grüne Lichter, hat aber keine der beiden echten Blockaden erkannt. Er testete ein WebSocket von einer Seite http://localhost — also ohne Mixed Content — und stellte keine API-Anfrage. Man muss Crypto, WebSocket und eine echte API-Anfrage in der endgültigen Schemakonfiguration validieren, nicht isoliert.


Was noch zu tun ist

Los 2 — Touch-Ergonomie: Niedrige Tab-Leiste, Android-Rückgesten, Finger-Ziehen-und-Ablegen des Dashboards, Verhalten der Tastatur.

Los 3 — Benachrichtigungen und Präsenz. Dies ist der Hauptbeitrag der App im Vergleich zum Browser und der einzige Los, der Entwicklung auf Serverseite erfordert: eine Route zur Registrierung von Push-Token und eine Szenenaktion user.send-push-notification.

Ein Punkt, der kollektiv entschieden werden muss: Eine selbstgehostete Instanz kann nicht direkt mit FCM oder APNs kommunizieren, mangels Dienstschlüsseln — die man natürlich nicht in einem öffentlichen Image verteilen kann. Der Relay über Gladys Plus ist der natürliche Weg (Inhalt wird Ende-zu-Ende verschlüsselt, die Benachrichtigung trägt nur einen Auslöser), mit einem Fallback auf lokale Benachrichtigungen ohne Abonnement. Ist dies ein akzeptabler Kompromiss für die Community?

Los 4 — Systemintegration: mDNS-Entdeckung (der Server veröffentlicht bereits den Dienst), Startbildschirm-Widgets, iOS-App-Intents und Android-Shortcuts, Sprachassistent.

Los 5 — Veröffentlichung: CI für Build und Signatur, Store-Einträge, Beta.


Zwei offene Punkte, bei denen ich Hilfe benötige

iOS konnte nicht validiert werden: Ich habe keinen Mac. Vier Unbekannte bleiben — gesicherter Kontext auf capacitor://, MSE in WKWebView, Multicast-Entitlement für mDNS (von Apple fallweise genehmigt, mit unvorhersehbaren Verzögerungen) und das Verhalten von ATS gegenüber einer HTTP-Instanz. Wenn jemand einen Mac und eine halbe Stunde Zeit hat, das Validierungsprototype laufen zu lassen, würde dies diese vier Punkte auf einmal klären.

Die Frage des lokalen HTTPS. Sie beeinflusst die Nutzung von Gladys Plus aus der App. Die Forderung nach einer HTTPS-Instanz ist technisch sauber, fügt aber eine Konfigurationsstufe für Benutzer hinzu, die dies heute nicht benötigen. Ich bin an euren Meinungen interessiert.


Die vollständige Spezifikation ist im Anhang unten (falten Sie den Abschnitt auf): Architektur, Verbindung, native Fähigkeiten, Sicherheit, harte Punkte, Lieferlose, Build und Abnahme. Der Code befindet sich in einem dedizierten Branch.

Zögern Sie nicht, die Architekturentscheidung zu hinterfragen — genau jetzt ist es noch einfach, sie zu ändern.

Vielen Dank fürs Lesen.


[details=« 📄 Vollständige technische Spezifikation (aufklappen) »]

Technische Spezifikation — v1

Die bestehende Gladys-Frontend (Preact / Vite) in eine native Android- und iOS-App über Capacitor portieren, mit voller Funktionsparität und den Fähigkeiten, die nur eine native App bieten kann: Push-Benachrichtigungen, Hintergrund-Geo-Lokalisierung, biometrische Entsperrung, Instanzentdeckung im lokalen Netzwerk.

Codebasis Gladys 5.0.2
Ansatz Capacitor + bestehende Frontend
Ziele Android 8+ · iOS 15+
Umfang v1 Volle Parität
Datum 30. August 2026

Inhaltsverzeichnis

  1. Kontext und Ziele
  2. Ist-Zustand
  3. Zielarchitektur
  4. Verbindung und Authentifizierung
  5. Native Fähigkeiten
  6. Schnittstellenanpassungen
  7. Sicherheit
  8. Identifizierte harte Punkte
  9. Lieferlose
  10. Build, CI und Veröffentlichung
  11. Tests und Abnahme
  12. Außerhalb des Umfangs der v1

1. Kontext und Ziele

Gladys Assistant wird heute mobil über den Browser oder das Hinzufügen zum Startbildschirm genutzt. Dieser Ansatz stößt an seine Grenzen: keine zuverlässigen Benachrichtigungen, kein Hintergrund-Präsenz-Tracking, keine Präsenz in den Stores und ein Service Worker, der sich im aktuellen Zustand des Repositories bei jeder Aktivierung absichtlich deinstalliert.

Ziele

  • Funktionsparität mit dem Web-Frontend: Dashboard, Geräte, Szenen, Kameras, Chat, Kalender, Karte, Einstellungen und Integrationen.
  • Echte Push-Benachrichtigungen, die von Gladys-Szenen ausgelöst werden, mit schnellen Aktionen aus der Benachrichtigung.
  • Automatische Präsenz durch Hintergrund-Geo-Lokalisierung, die die Gladys-Zonen-Erkennung speist.
  • Reibungslose Verbindung: Automatische Instanzentdeckung im lokalen Netzwerk, transparentes Umschalten lokal ↔ Gladys Plus.
  • Eine einzige Codebasis für Web, Android und iOS: Jede Frontend-Änderung profitiert allen drei Zielen ohne Portierung.

Leitprinzipien

  • Der Respekt vor der Privatsphäre bleibt die Regel. Keine Telemetrie, keine Drittanbieter-Analytik-SDKs. Die Ende-zu-Ende-Verschlüsselung von Gladys Plus wird ohne Ausnahme beibehalten.
  • Das Frontend bleibt die einzige Wahrheit. Die native Hülle enthält keine Geschäftslogik: Sie bietet Fähigkeiten, das Frontend nutzt sie.
  • Saubere Abwärtskompatibilität. Jede native Funktion ist optional; ihr Fehlen (abgelehnte Berechtigung, nicht kompatible Plattform) bricht die App nie.

2. Ist-Zustand

Das Frontend ist eine Preact 10-Anwendung, die von Vite 6 gebaut wird, mit preact-router für die Routing, unistore für den globalen Zustand und preact-i18n für die Internationalisierung in drei Sprachen (fr, en, de). Sie wird entweder vom lokalen Gladys-Server (server/static) oder von Gladys Plus bereitgestellt.

Was direkt wiederverwendet werden kann

Element Standort Status
Bildschirme und Routing front/src/routes/ Unverändert
Dashboard-Widgets (22 Typen) front/src/components/boxs/ Unverändert
Aktionen und unistore front/src/actions/ Unverändert
Übersetzungen fr / en / de front/src/config/i18n/ Unverändert
Thema und dunkler Modus front/src/style/ Unverändert

Was angepasst werden muss

Element Standort Art der Arbeit
URL-Konfiguration front/src/config.js Die URLs sind zum Zeitpunkt des vite build über process.env festgelegt. Sie müssen zur Laufzeit dynamisch gemacht werden. Harter Punkt
Sitzungsspeicher front/src/utils/Session.js, front/src/utils/keyValueStore.js localStorage im Klartext. Muss durch einen verschlüsselten Speicher ersetzt werden, der an den Keychain / Keystore angebunden ist. Harter Punkt
HTTP-Client front/src/utils/HttpClient.js :white_check_mark: Erledigt (Los 1). localApiUrl wurde im Konstruktor erfasst, ausgeführt beim Laden des Moduls über getDefaultState() — also vor der Auflösung des mobilen Profils. Jetzt ein Getter, der die Konfiguration bei jeder Anfrage liest. Im Web ändert sich der Wert nie: identisches Verhalten.
Sichere Bereiche front/index.html, front/src/template.html :white_check_mark: Erledigt (Los 1). Das <meta viewport> nEs fehlte viewport-fit=cover, wodurch alle Variablen env(safe-area-inset-*) auf null gesetzt wurden — einschließlich der fünf vorhandenen Verwendungen von safe-area-inset-bottom.
WebSocket front/src/utils/Session.js Wird alle 1 Sekunde ohne Berücksichtigung des Anwendungslebenszyklus neu verbunden. Es sollte eine Backoff-Strategie und eine Reaktion auf Schlafereignisse hinzugefügt werden.
Service Worker front/old-sw.js Deinstalliert sich selbst bei der Aktivierung. Wird nicht in der nativen Shell verwendet; sollte für das Web beibehalten werden, um alte Caches zu bereinigen.
Kameras (HLS) front/src/components/boxs/camera/ Benutzerdefinierter hls.js-Lader zum Einfügen des Tokens. Muss in der WebView validiert werden, insbesondere das native HLS auf iOS.
Drag-and-Drop front/src/utils/dragAndDropBackend.js Wechselt bereits zwischen HTML5- und Touch-Backends; muss in der WebView neu validiert werden.

Serverseitig

Für die Lose 1 und 2 sind keine Änderungen erforderlich: Die REST-API (server/api/controllers/, 25 Controller) und das WebSocket decken bereits die Anforderungen. Zwei Servererweiterungen sind später erforderlich: die Registrierung der Push-Tokens (Los 3) und die entsprechende Szenenaktion.


3. Zielarchitektur

Die Frontend wird genau wie heute kompiliert und dann mit Capacitor in eine native WebView verpackt. Die Fähigkeiten, die das Web nicht bieten kann, werden dem Frontend in Form von Capacitor-Plugins hinter einer Abstraktionsschicht bereitgestellt, die neutrale Werte zurückgibt, wenn die App in einem Browser läuft.

┌─────────────────────────────────────┐
│ MOBILE APPLIKATION                 │
│                                     │        ┌──────────────────────────────┐
│  ┌───────────────────────────────┐  │        │ Lokales Netzwerk             │
│  │ WebView — front Preact        │  │───────>│ REST /api/v1 + WebSocket     │
│  │ routes/ · components/         │  │        │ Trägertoken, minimale Latenz │
│  │ ─────────────────────────────  │  │        │ mDNS-Entdeckung              │
│  │ utils/native/ — Abstraktion   │  │        └──────────────┬───────────────┘
│  │ no-op im Web                  │  │                       │
│  └───────────────┬───────────────┘  │        ┌──────────────▼───────────────┐
│                  ▼                  │        │ Gladys Plus — Remote         │
│  ┌───────────────────────────────┐  │───────>│ gladys-gateway-js            │
│  │ Natives Capacitor-Shell       │  │        │ RSA + ECDSA, Ende-zu-Ende    │
│  │ push · Geolokation · Biometrie│  │        │ Der Server entschlüsselt nichts│
│  │ mDNS · Speicher · Widgets      │  │        └──────────────┬───────────────┘
│  └───────┬───────────────┬───────┘  │                       │
└──────────┼───────────────┼──────────┘                       │
           ▼               ▼                   ┌──────────────▼───────────────┐
    ┌────────────┐  ┌────────────┐             │ Gladys-Instanz              │
    │ Android    │  │ iOS        │             │ Node 24 · SQLite             │
    │ Kotlin·FCM │  │ Swift·APNs │             │ 39 Dienste                   │
    └────────────┘  └────────────┘             └──────────────────────────────┘

Die Auswahl des Netzwerkpfads erfolgt automatisch und wird bei jedem Netzwerkwechsel neu bewertet; der Benutzer kann sie über die Einstellungen erzwingen. Das kompilierte Frontend ist identisch mit dem, das im Web bereitgestellt wird: Nur die Schicht utils/native/ wird hinzugefügt und gibt außerhalb von mobilen Geräten neutrale Implementierungen zurück.

Verzeichnisstruktur

Ein neues Verzeichnis mobile/ an der Wurzel, neben front/ und server/:

mobile/
├── capacitor.config.ts     Konfiguration, verweist auf front/build
├── package.json            Nur Capacitor-Abhängigkeiten
├── android/                Generiertes Android-Projekt, versioniert
├── ios/                    Generiertes Xcode-Projekt, versioniert
├── plugins/                Eigenen Plugins (mDNS, Entdeckung)
└── resources/              Quellen für Icons und Startbildschirme

front/src/utils/native/     Abstraktionsschicht, im Frontend
├── index.js                Plattformerkennung
├── push.js
├── geolocation.js
├── biometrics.js
├── secureStorage.js
└── discovery.js

Ausgewählte Plugins

Bedarf Plugin Herkunft
Push-Benachrichtigungen @capacitor/push-notifications Offiziell
Lokale Benachrichtigungen @capacitor/local-notifications Offiziell
Geolokalisierung (einmalig) @capacitor/geolocation Offiziell
Geolokalisierung (Hintergrund) @capacitor-community/background-geolocation Community
Verschlüsselter Speicher capacitor-secure-storage-plugin Community
Biometrie @aparajita/capacitor-biometric-auth Community
Netzwerkstatus @capacitor/network Offiziell
App-Lebenszyklus @capacitor/app Offiziell
Statusleiste und Kerben @capacitor/status-bar Offiziell
mDNS-Erkennung gladys-discovery Zu schreiben

Designentscheidung — Jedes Community-Plugin birgt ein Wartungsrisiko. Die Schicht utils/native/ existiert genau dafür, dass der Ersatz eines veralteten Plugins nur eine einzige Datei betrifft, niemals die Bildschirme.


4. Verbindung und Authentifizierung

Dies ist der strukturierendste Teil der Spezifikation. Die aktuelle Frontend-Anwendung kennt nur einen Modus gleichzeitig, der zum Zeitpunkt des Builds durch die Variable GATEWAY_MODE festgelegt wird. Die mobile App muss beide gleichzeitig verwalten und ohne Benutzereingriff zwischen ihnen wechseln.

4.1 Laufzeitkonfiguration

front/src/config.js liest process.env, das Vite zum Zeitpunkt des Builds durch Literale ersetzt. Es muss eine Laufzeitauflösung eingeführt werden:

  • Im Web bleibt das aktuelle Verhalten unverändert: keine Rückschritte.
  • Auf mobilen Geräten stammen die URLs vom aktiven Verbindungsprofil, das im verschlüsselten Speicher gespeichert ist.

Konkrekt stellt config.js ein veränderliches Objekt bereit, das beim Start durch utils/native/ gefüllt wird, bevor die erste Darstellung erfolgt. Die Module, die die Konfiguration heute importieren, bleiben unverändert.

4.2 Verbindungsprofile

Die App verwaltet mehrere Profile — ein Hauptzuhause, ein Zweitwohnsitz, eine Testinstanz. Jedes Profil speichert:

Feld Inhalt
id Lokale UUID
name Angezeigter Name, vom Benutzer eingegeben
mode local, gateway oder auto
localUrl URL der Instanz im lokalen Netzwerk
localFingerprint Fingerabdruck des Zertifikats, falls selbstsigniertes HTTPS
ssids WLAN-Netzwerke, in denen der lokale Modus gilt
credentials Referenz zum verschlüsselten Speicher, niemals der Wert

4.2 bis Die drei URLs von Gladys Plus

Nicht zu verwechseln — sie haben unterschiedliche Rollen:

URL Rolle Verwendet von
https://api.gladysgateway.com Die API des Gateways config.gladysGatewayApiUrl, aufgerufen von gladys-gateway-js
https://plus.gladysassistant.com Das Web-Frontend von Gladys Plus (derselbe Code, im Gateway-Modus) Ausgehende Links: Abonnement, Abrechnung
https://gladysassistant.com/plus/ Marketing-Seite utils/gladysPlusUrl.js (Anmelde-Links)

Die mobile App kommuniziert mit der API, niemals mit dem gehosteten Frontend: sie ist das Frontend.
Der Standardwert von config.js ist daher bereits korrekt und muss nicht
geändert werden.

Andererseits müssen die Abläufe, die nicht in der App ablaufen können — Abonnementverwaltung, Stripe-Abrechnung — plus.gladysassistant.com im Systembrowser öffnen und nicht in der WebView: ein Zahlungstunnel in einer Anwendungs-WebView wird von beiden Stores abgelehnt. Das Plugin
@capacitor/browser (System-Tab) ist das richtige Fahrzeug.

4.3 Erkennung der lokalen Instanz

Der Gladys-Server veröffentlicht bereits einen mDNS-Dienst (server/lib/mdns/). Das zu schreibende Plugin gladys-discovery fragt diesen Dienst ab und zeigt die gefundenen Instanzen an:

  • Android: NsdManager, mit Erwerb eines MulticastLock während der Suche.
  • iOS: NWBrowser (Network-Framework). Erfordert das Entitlement com.apple.developer.networking.multicast, das explizit bei Apple angefordert werden muss, und die Deklaration NSBonjourServices in Info.plist.

Falls die Erkennung fehlschlägt, bleibt die manuelle Eingabe einer Adresse immer möglich: sie ist niemals ein versteckter Ausweichweg, sondern eine Option, die bereits auf dem ersten Bildschirm sichtbar ist.

4.4 Wechsel lokal ↔ entfernt

Im Modus auto wählt die App den Netzwerkpfad bei jedem Start, bei jeder Rückkehr in den Vordergrund und bei jeder von @capacitor/network gemeldeten Verbindungsänderung:

  1. Wenn ein lokales Profil konfiguriert ist, wird ein GET /api/v1/ping mit einer Schutzverzögerung von 1,5 Sekunden versucht.
  2. Bei Erfolg wird der lokale Modus beibehalten: minimale Latenz, keine externe Abhängigkeit.
  3. Bei Fehlschlag und wenn ein Gladys Plus-Konto verknüpft ist, wechselt die App zum Gateway.
  4. Wenn kein Pfad antwortet, wird ein Offline-Bildschirm mit den letzten bekannten Daten und einem Wiederaufnahme-Button angezeigt.

Der Wechsel rekonstruiert den HTTP-Client und die WebSocket-Verbindung. Er wird diskret in der Benutzeroberfläche angezeigt (ein Indikator in der Kopfzeile), niemals durch ein blockierendes Dialogfeld.

Achtung — Die beiden Modi verwenden nicht denselben Token-Speicher noch denselben Client: Session + HttpClient für lokal, GatewaySession + GatewayHttpClient für Gladys Plus. Der Wechsel muss die Caches der fliegenden Anfragen von HttpClient (die Map pendingRequests) leeren, um zu verhindern, dass eine Antwort aus dem alten Pfad dem neuen zugeordnet wird.

4.5 End-to-End-Verschlüsselung

GatewaySession stützt sich auf @gladysassistant/gladys-gateway-js, das window.crypto erhält. In einer WebView ist die Web Crypto API nur in einem sicheren Kontext verfügbar: die Schemata capacitor:// (iOS) und https:// (Android) sind dies, im Gegensatz zu http://. Dieser Punkt muss bereits im ersten Los validiert werden, da er den gesamten Fernzugriff bedingt.

Die serialisierten Schlüssel (gateway_serialized_keys) befinden sich heute in localStorage. Auf mobilen Geräten gehen sie in den verschlüsselten Speicher, der an den Keychain (iOS) oder den Keystore mit Hardware-Verschlüsselung (Android) angelehnt ist.

4.6 Zwei-Faktor-Authentifizierung

Der bestehende 2FA-Ablauf (actions/login/loginGateway.js) bleibt unverändert, einschließlich der Generierung der Wiederherstellungscodes und des Einfügens aus der Zwischenablage. Ein automatisches Ausfüllen des Codes aus den Tastaturvorschlägen wird über das Attribut autocomplete="one-time-code" hinzugefügt.

4.7 Biometrische Sperre

Optional, aktivierbar in den Einstellungen. Wenn es aktiviert ist, wird beim Start und nach einer konfigurierbaren Inaktivitätsdauer (standardmäßig 5 Minuten im Hintergrund) eine Entsperrung per Fingerabdruck oder Gesichtserkennung angefordert. Die Ausweichmöglichkeit ist der Gerätecode; es gibt niemals einen eigenen Gladys-Code, den man zusätzlich merken muss.


5. Native Fähigkeiten

5.1 Push-Benachrichtigungen

Dies ist der Hauptbeitrag der App im Vergleich zum Web. Er erfordert eine Entwicklung auf der Gladys-Server-Seite und nicht nur auf der mobilen Seite.

Registrierung. Beim ersten Start nach Akzeptanz der Berechtigung erhält die App ein FCM-Token (Android) oder APNs-Token (iOS) und sendet es an die Instanz. Neue Route zu erstellen: POST /api/v1/user/push_token, mit dem Token, der Plattform und der Sitzungs-ID. Das Token ist mit der bestehenden Sitzung verknüpft: Das Widerrufen einer Sitzung widerruft auch den zugehörigen Push.

Versand. Eine selbstgehostete Gladys-Instanz kann nicht direkt mit FCM oder APNs kommunizieren, ohne Dienstschlüssel — die nicht in einem öffentlichen Image verteilt werden können. Zwei Wege:

  • Über Gladys Plus (empfohlen) — Die Instanz überträgt die Benachrichtigung an das Gateway, das die Schlüssel besitzt und an FCM / APNs weiterleitet. Der nützliche Inhalt ist Ende-zu-Ende verschlüsselt; die transportierte Benachrichtigung enthält nur einen Auslöser, die App holt den eigentlichen Inhalt von der Instanz beim Empfang ab.
  • Ohne Gladys Plus — Ausweichen auf lokale Benachrichtigungen: Solange die WebSocket-Verbindung aktiv ist, plant die App selbst eine Benachrichtigung. Funktioniert im kürzlichen Hintergrund, nicht nach einem längeren Ruhezustand. Diese Einschränkung muss in den Einstellungen klar angekündigt werden, nicht erst beim Gebrauch entdeckt.

Auslösung von einer Szene. Eine neue Szenenaktion user.send-push-notification wird hinzugefügt, mit Empfängern, Titel, Nachricht und optional einem Kamerabild. Sie muss zwingend im Joi-Schema von server/models/scene.js deklariert werden, andernfalls schlägt die Szenenregistrierung mit einem 422-Fehler ohne explizite Nachricht fehl.

Schnellaktionen. Die Benachrichtigungen enthalten bis zu drei Aktionsbuttons, die in der Szene definiert sind: Ausführung einer anderen Szene, Ein- oder Ausschalten eines Geräts, Öffnen einer Kamera. Diese Aktionen werden ohne Öffnen der App durchgeführt, wenn das Netzwerk es zulässt.

5.2 Geolokalisierung und Anwesenheit

Gladys verfügt bereits über server/lib/location/ und die Verwaltung von Zonen. Die App versorgt POST /api/v1/location:

  • Zonenbasierte Verfolgung anstelle kontinuierlicher Verfolgung: Die App abonniert Ein- und Ausgänge der in Gladys definierten Zonen. Der Akkuverbrauch ist deutlich geringer als bei periodischen Abfragen.
  • Reduzierte Genauigkeit standardmäßig: Die Position wird nur beim Passieren einer Zone übertragen, nicht kontinuierlich.
  • Offline-Warteschlange: Ereignisse, die ohne Netzwerk erfasst wurden, werden lokal gespeichert und bei der Wiederverbindung mit dem tatsächlichen Zeitstempel gesendet.
  • Globaler Schalter, sichtbar in den Einstellungen, und sofortige Beendigung der Verfolgung, wenn er ausgeschaltet wird.

Store-Beschränkung — Die Hintergrund-Geolokalisierung ist der häufigste Ablehnungsgrund im App Store. Es wird benötigt: eine klare Erklärung vor der Berechtigungsanfrage, eine präzise Begründung in NSLocationAlwaysAndWhenInUseUsageDescription und eine voll funktionsfähige App, wenn die Berechtigung verweigert wird. Auf Android 13+ wird die Berechtigung ACCESS_BACKGROUND_LOCATION in einem zweiten Schritt nach der Vordergrundberechtigung angefordert.

5.3 Widgets für den Startbildschirm

Nativ geschrieben: WidgetKit in SwiftUI auf iOS, Glance auf Android. Sie lesen einen von der App in einem gemeinsamen Speicherbereich (App Group auf iOS, SharedPreferences auf Android) geschriebenen Datenschnappschuss, der bei jedem Wechsel in den Vordergrund und bei jeder Benachrichtigung aktualisiert wird.

  • Szenen: Bis zu vier favorisierte Szenen, die mit einem Tastendruck ausgeführt werden können.
  • Temperatur: Temperatur eines ausgewählten Raums.
  • Geräte: Zustand und Umschaltung von zwei bis vier Geräten.

Die Widgets öffnen keine eigene Sitzung: Sie delegieren an die App, die die Aktion ausführt. Wenn die Sitzung abgelaufen ist, zeigt das Widget einen Zustand der erforderlichen Wiederverbindung an, anstatt einen Fehler.

5.4 Systemverknüpfungen

  • iOS: Szenen werden in App Intents exponiert, wodurch sie in Kurzinfo, Siri und der Aktionsschaltfläche verfügbar sind.
  • Android: Dynamische Verknüpfungen auf dem App-Symbol für favorisierte Szenen.

5.5 Sprachassistent

Die Frontend-Schnittstelle verfügt bereits über eine vollständige Sprachkette: speechCommandRecorder.js, recordUntilSilence.js, speechTtsPlayback.js, gestützt auf gateway.stt.js und gateway.processVoiceMessage.js auf Serverseite.

In der WebView erfordert der Mikrofonzugriff die native Berechtigung (NSMicrophoneUsageDescription, RECORD_AUDIO) und die Erlaubnis auf WebView-Ebene. Auf Android erfolgt Letzteres über onPermissionRequest, das im nativen Shell behandelt werden muss. Die TTS-Wiedergabe muss die Audio-Sitzungskategorie auf iOS konfigurieren, um nicht durch den Stummschaltmodus unterbrochen zu werden.

5.6 Kameras

Der HLS-Stream wird über hls.js mit einem benutzerdefinierten Lader gelesen, der das Authentifizierungstoken injiziert. In der WebView:

  • Auf iOS wird der native HLS-Player von <video>deo> erlaubt nicht das Hinzufügen von Headern; hls.js über MSE bleibt daher notwendig. Die MSE-Kompatibilität in WKWebView muss frühzeitig überprüft werden.
  • Der Videovollbildmodus erfordert allowsInlineMediaPlayback und eine explizite Verwaltung der Drehung.
  • Die Wiedergabe muss beim Wechsel in den Hintergrund unterbrochen und beim Zurückkehren fortgesetzt werden, um Batterie und Daten nicht unnötig zu verbrauchen.

6. Benutzeroberflächenanpassungen

Die Benutzeroberfläche bleibt die des Web-Frontends. Die Anpassungen sind gezielt, und keine davon darf das Erlebnis im Browser verschlechtern.

Sichere Bereiche und Kerben. Anwendung von env(safe-area-inset-*) auf die Kopfzeile, die untere Navigation und die Modals. Der viewport wird in front/src/template.html auf viewport-fit=cover gesetzt, ohne Auswirkungen auf das Web.

Navigation. Die aktuelle Seitenleiste wird auf schmalen Bildschirmen zu einer unteren Tab-Leiste: Dashboard, Geräte, Szenen, Chat, Einstellungen. Die Android-Rückgeste wird über @capacitor/app an preact-router gebunden, mit einer Bestätigung zum Verlassen nur an der Wurzel.

Berührungsziele und Gesten.

  • Jedes interaktive Ziel ist mindestens 44 × 44 Punkte groß.
  • Das Ziehen und Ablegen im Dashboard verwendet das Touch-Backend von react-dnd, das bereits vorhanden ist, mit einem langen Druck, um das Verschieben zu aktivieren, um nicht mit dem Scrollen in Konflikt zu geraten.
  • Zum Aktualisieren ziehen auf Listenbildschirmen, deaktiviert während einer laufenden Bearbeitung.

Tastatur. Anpassung der Ansicht beim Öffnen der Tastatur anstelle von Überlagerung, angepasste Eingabetypen (inputmode="numeric" für Codes, type="email"), und automatisches Scrollen zum aktiven Feld.

Dunkler Modus. Das dunkle Thema von Gladys basiert auf einer globalen CSS-Invertierung, mit der Klasse dark-mode-no-invert für Elemente, die ihre echten Farben behalten müssen. Dieser Mechanismus bleibt unverändert. Zwei neue Punkte: Die Farbe der nativen Statusleiste muss dem Thema folgen, und die Invertierung darf nicht auf dem nativen Startbildschirm angewendet werden.

Tablet-Modus. Der bestehende Tablet-Modus (routes/dashboard/SetTabletMode.jsx) macht auf einem Wandtablet Sinn: Hier wird das Bildschirm-On-Halten, die Ausrichtungssperre und ein immersiver Vollbildmodus hinzugefügt. Die bestehende Codesperre bleibt erhalten.


7. Sicherheit

Speicherung von Geheimnissen

Keine Geheimnisse bleiben im localStorage auf mobilen Geräten. Migration zur verschlüsselten Speicherung: Zugriffs- und Aktualisierungstoken, serialisierte Gladys Plus-Schlüssel, Fingerabdrücke öffentlicher Schlüssel, Zwei-Faktor-Authentifizierungstoken. Nicht-sensible Einstellungen bleiben in der normalen Speicherung: Sprache, dunkler Modus, ausgewähltes Zuhause.

HTTP im Klartext im lokalen Netzwerk

Eine lokale Gladys-Instanz wird sehr oft über http:// bereitgestellt. Android blockiert jedoch standardmäßig den Klartextverkehr seit Version 9, und iOS über ATS.

Plattform Mechanismus Geltungsbereich
Android network_security_config.xml Erlaubnis für Klartextverkehr beschränkt auf private Bereiche: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, plus .local
iOS NSAllowsLocalNetworking ATS-Ausnahme begrenzt auf das lokale Netzwerk, ohne globale Deaktivierung

Es ist niemals die Rede von NSAllowsArbitraryLoads oder cleartextTrafficPermitted global: Diese beiden Einstellungen deaktivieren den Schutz im gesamten Internet und sind ein Grund für die Ablehnung durch die Apple-Prüfung.

Selbstsignierte Zertifikate

Für eine lokale HTTPS-Instanz mit selbstsigniertem Zertifikat bietet die App das „Pinning“ des Zertifikatsfingerabdrucks beim Hinzufügen des Profils an, mit Anzeige des Fingerabdrucks zur Überprüfung. Keine blinde Annahme; eine Änderung des Fingerabdrucks blockiert die Verbindung und erfordert eine explizite Bestätigung.

Inhaltsschutz

  • Verstecken des Inhalts im App-Selektor (FLAG_SECURE optional auf Android, Overlay-Ansicht auf iOS), aktivierbar in den Einstellungen.
  • Keine Screenshots von Kamerastreams in Systemvorschauen, wenn die Option aktiviert ist.
  • Vollständiges Löschen der verschlüsselten Speicherung beim Abmelden.

Was die App nicht tut

  • Kein SDK für Analysen, Werbung oder Tracking.
  • Keine automatischen Crash-Berichte von Dritten; bei Bedarf eine manuelle, explizite und zustimmende Übermittlung.
  • Keine Daten des Hauses werden über einen anderen Server als die Instanz des Benutzers oder Gladys Plus übertragen, deren Inhalt Ende-zu-Ende verschlüsselt ist.

8. Identifizierte harte Punkte

Diese sieben Punkte sind diejenigen, die den Zeitplan durcheinanderbringen können. Sie werden absichtlich so früh wie möglich in der Planung platziert, damit ihre tatsächlichen Kosten bekannt sind, bevor in den Rest investiert wird.

Punkt Risiko Behandlung
Web Crypto in WebView Wenn window.crypto.subtle nicht verfügbar ist, fällt der gesamte entfernte Gladys Plus-Zugriff aus. Validierungsprototype in der ersten Woche auf beiden Plattformen. Die Schemata capacitor:// und https:// sind sichere Kontexte: Das Risiko ist gering, aber die Auswirkungen sind insgesamt groß.
Konfiguration fest beim Build config.js wird von Vite beim Build aufgelöst: Die App kann die Instanz nicht ändern. Überarbeitung in eine Konfiguration, die zur Laufzeit aufgelöst wird, mit strikter Beibehaltung des aktuellen Web-Verhaltens.
Push ohne Gladys Plus Eine selbstgehostete Instanz hat keine FCM-/APNs-Schlüssel. Weiterleitung über das Gateway für Gladys Plus-Konten; lokale Benachrichtigungen als Ausweichlösung, mit ihren explizit angekündigten Grenzen.
Multicast iOS Die mDNS-Entdeckung erfordert ein Entitlement, das von Apple fallweise gewährt wird, mit unvorhersehbaren Verzögerungen. Antragstellung bei der Eröffnung des Entwicklerkontos. Die manuelle Eingabe der Adresse ist ein erster Klasse-Pfad, kein Ausweichweg.
WebSocket im Hintergrund Beide Betriebssysteme trennen die Verbindungen im Ruhezustand; der angezeigte Zustand kann beim Aufwachen veraltet sein. Wiederverbindung mit Backoff beim Rückkehr in den Vordergrund, vollständiges Neuladen des Zustands und Indikator für die Aktualität der Daten.
HLS in WKWebView Die MSE-Wiedergabe über hls.js kann sich anders verhalten als in Safari. Test auf echtem Gerät im Los 1, vor jedem Engagement im Los 4.
Mixed content (bei Test entdeckt) Die über https://localhost bereitgestellte WebView kann weder http:// noch ws:// auf der lokalen Instanz aufrufen: Chromium blockiert. Die Android-Netzwerksicherheitskonfiguration ändert nichts daran — es ist eine Richtlinie des Browsers, nicht der Plattform. CapacitorHttp löst den Fall der HTTP-Anfragen, aber nicht den der WebSockets, die weiterhin der Regel unterliegen. Siehe die Entscheidung unten: Los 1 geht mit androidScheme: "http".

Entscheidung: Schema der WebView

Die Wahl des Schemas stellt zwei Fähigkeiten gegenüber, die man nicht gleichzeitig erreichen kann, solange die Instanz in HTTP ist:

androidScheme Sicherer Kontext Crypto / Gladys Plus Lokales HTTP Lokales WebSocket
https ja OK über CapacitorHttp BLOCKIERT
http nein nicht verfügbar OK OK

Entscheidung für Los 1: http. Los 1 zielt auf die Verbindung zur lokalen Instanz ab, die WebSockets erfordert; Gladys Plus ist noch nicht angeschlossen. Diese Wahl ist ausdrücklich vorübergehend.

Muss zwingend vor Los Gladys Plus überarbeitet werden. Zwei Wege:

  1. Instanz in HTTPS (TLS-Reverse-Proxy: Caddy, nginx, Traefik). Alles läuft über https/wss, keine Blockaden mehr, sicherer Kontext bleibt erhalten. Dies ist die einzige Lösung, die auch für iOS gilt, wo das Schema capacitor:// die gleichen Einschränkungen auferlegt. Empfohlener Weg.
  2. Natives WebSocket-Plugin, das die Verbindung außerhalb der WebView öffnet, wie CapacitorHttp es für Anfragen tut. Ermöglicht das Bleiben bei https, erfordert jedoch die Änderung von Session.js, das mit dem Web geteilt wird.

Praktische Konsequenz: Solange das Los 1 in androidScheme: "http" ist,
ist crypto.subtle in der App nicht verfügbar und jeder Versuch, sich mit
Gladys Plus zu verbinden, wird scheitern. Der Validierungsprototyp hingegen
maß die Krypto in https — die Fähigkeit der Plattform ist erworben, es ist ihre
Koexistenz mit einem Backend im Klartext, die es nicht ist.
| App Store Review | Hintergrund-Geo-Lokalisierung und die sogenannte „Client einer Dienstleistung“ sind zwei klassische Ablehnungsgründe. | Demoregelung für Prüfer ohne Instanz, sorgfältig formulierte Berechtigungsbegründungen, Screenshots der tatsächlichen Nutzung der Bereiche. |


9. Lieferlose

Die Lose sind tatsächlich sequenziell: Jedes baut auf dem vorherigen auf und endet mit einem auf einem echten Gerät installierbaren Lieferobjekt. Los 1 konzentriert sich absichtlich auf die technischen Risiken.

Los 1 — Grundlagen und Authentifizierung :white_check_mark: GELIEFERT

Ziel: Nachweisen, dass der Ansatz funktioniert, und die kritischen Punkte 1, 2 und 6 lösen.

Commit feat(mobile): resolve the instance to connect to at runtime —
22 Dateien, 1254 Einfügungen, 14 Löschungen.

  • :white_check_mark: Konfiguration zur Laufzeit aufgelöst (setRuntimeConfig, Whitelist von Schlüsseln)
  • :white_check_mark: Schicht utils/native/ — durch Tree-Shaking aus dem Web-Bundle entfernt
  • :white_check_mark: Sichere Speicherung (Keychain / Keystore, Fallback localStorage im Web)
  • :white_check_mark: Multi-Instanz-Profile: Liste, Hinzufügen, Bearbeiten, Löschen, Umschalten
  • :white_check_mark: Erster Verbindungsbildschirm mit Adresstest vor der Registrierung
  • :white_check_mark: Einstellungen „Instanzen“ — im Web versteckt (nativeOnly)
  • :white_check_mark: Anzeige für verlorene Verbindung im Header
  • :white_check_mark: Sichere Bereiche: viewport-fit=cover + oberer Balken und Navigationsschublade
  • :white_check_mark: Manuell überprüfte Web-Nicht-Rückwirkung
  • :warning: Gladys Plus-Verbindung nicht funktionsfähig — siehe die Abwägung des Schemas, Abschnitt 8
  • :cross_mark: iOS-Projekt nicht generiert: Keine macOS-Maschine verfügbar

Auf Gerät validiert (Xiaomi, Android 16, WebView Chromium 151): Vollständiger Konfigurationsablauf, Verbindung zur lokalen Instanz, Login, Dashboards, Kameras.

Kontrollen: Die drei CI-Checks sind bestanden (Prettier, ESLint 0 Fehler, Übersetzungsparität fr/en/de), plus 43 Logiktests, die auf dem echten Code des Repositories ausgeführt wurden.

Los 2 — Funktionale Parität und taktile Ergonomie

Ziel: Eine App, die im Alltag nutzbar ist, aber noch ohne native Funktionen.

  • Sichere Bereiche, niedrige Tab-Leiste, Android-Rückgesten
  • Dashboard-Ziehen und Ablegen mit dem Finger validiert
  • Verhalten der Tastatur, Ziehen zum Auffrischen, taktile Ziele
  • Statusleiste und Startbildschirm im Dunkelmode abgestimmt
  • Bildschirm-für-Bildschirm-Überprüfung der 23 bestehenden Routen auf Telefon und Tablet

Los 3 — Benachrichtigungen und Präsenz

Ziel: Der erste konkrete Grund, die App zu installieren, anstatt den Browser zu öffnen.

  • Server: Route POST /api/v1/user/push_token, verknüpft mit den Sitzungen
  • Server: Szenenaktion user.send-push-notification, inklusive Joi-Schema
  • Weiterleitung der Benachrichtigungen durch Gladys Plus, Ende-zu-Ende-verschlüsselter Inhalt
  • Lokale Benachrichtigungen als Fallback ohne Gladys Plus
  • Schnelle Aktionen in den Benachrichtigungen
  • Geo-Lokalisierung nach Zonen, Offline-Warteschlange, globaler Schalter
  • Biometrie und Sperre nach Inaktivität

Los 4 — Systemintegration

Ziel: Gladys ohne Öffnen der App zugänglich machen.

  • mDNS-Entdeckung: Plugin gladys-discovery, Android und iOS
  • Startbildschirm-Widgets: Szenen, Temperatur, Geräte
  • iOS App Intents und dynamische Android-Shortcuts
  • Sprachassistent: Mikrofonberechtigung in WebView, iOS-Audiositzung
  • Kameras: Vollbild, Rotation, Hintergrundstopp
  • Wand-Tablet-Modus: Bildschirm bleibt eingeschaltet, Ausrichtung gesperrt, immersives Vollbild

Los 5 — Veröffentlichung

Ziel: Die App ist über die Stores installierbar und aktualisiert sich selbst.

  • CI-Kette: Android- und iOS-Build und Signatur
  • Play Store- und App Store-Einträge: Beschreibungen, Screenshots, Datenschutzrichtlinie
  • Demomodus für Prüfer ohne Instanz erforderlich
  • Interne Tests, dann offene Beta: Play Console und TestFlight
  • Benutzerdokumentation und Beitragsdokumentation

Ablaufplanung — Los 1 und 2 produzieren bereits eine intern verteilbare App. Wenn sich die kritischen Punkte von Los 1 als teurer als erwartet herausstellen, erfolgt die Abwägung zu diesem Zeitpunkt, bevor die native Entwicklung von Los 3 begonnen wird.


10. Build, CI und Veröffentlichung

Build-Kette

Neue npm-Skripte an der Wurzel, in Ergänzung zu den bestehenden Skripten:

build-mobile      vite build für mobile, dann npx cap sync
mobile:android    öffnet das Projekt in Android Studio
mobile:ios        öffnet das Projekt in Xcode
mobile:live       Hot Reload auf verbundenem Gerät

Der mobile Build nutzt front/vite.config.mjs mit einem dedizierten Modus: Die URL-Konfiguration wird nicht mehr beim Build injiziert, und der Service Worker wird nicht kopiert.

Versionierung

Die App-Version folgt der von Gladys (package.json an der Wurzel, heute 5.0.2), mit einer inkrementellen Build-Nummer, die speziell für mobile Geräte ist. Die Android-versionCode und iOS-CFBundleVersion werden automatisch in der CI abgeleitet.

Instanzkompatibilität

Eine aktualisierte App kann sich mit einer älteren Instanz verbinden. utils/instanceVersion.js verwaltet bereits diese Erkennung im Web: Sie wird erweitert, um Funktionen, die eine minimale Serverversion erfordern, insbesondere Push, ordnungsgemäß zu deaktivieren, anstatt einen Aufruf ohne Erklärung scheitern zu lassen.

Kontinuierliche Integration

Ein dedizierter GitHub Actions-Workflow, getrennt von der bestehenden CI:

  • Bei jedem Pull Request, der mobile/ oder front/ betrifft: Android-Build im Debug-Modus, ohne Signatur.
  • Bei jedem Versions-Tag: Signierter Android- und iOS-Build, Hochladen auf die internen Testkanäle.
  • Signaturgeheimnisse in Repository-Geheimnissen; keine Schlüssel im Repository selbst.
  • Die bestehenden Überprüfungen bleiben für den Frontend-Bereich gültig: Prettier, dann ESLint, und 100 % Patch-Abdeckung für jeden hinzugefügten Server-Code.

Konten und Kosten

Posten Art Kosten
Apple-Entwicklerkonto Organisation 99 $ / Jahr
Google Play-Konto Organisation 25 $ einmalig
Firebase-Projekt Nur FCM Kostenlos
iOS-Build-Maschine macOS, CI oder lokal Variabel

11. Tests und Abnahme

Was die CI abdeckt

  • Die bestehenden Cypress-Tests laufen weiterhin auf dem Web-Frontend: Sie schützen vor Rückwirkungen, die durch die Neugestaltung der Konfiguration eingeführt werden.
  • Mocha-Einheitstests auf dem hinzugefügten Server-Code (Push-Token-Route, Szenenaktion), mit erzwungener TZ=UTC.
  • Der Android-Build im Debug-Modus dient als Rauchtest für die Capacitor-Integration.

Manuelle Abnahme auf Gerät

Eine Testmatrix wird für jedes Los aktualisiert. Szenarien, die nicht automatisiert werden können und die manuell überprüft werden müssen:

Szenario Was überprüft wird
Wi-Fi-Umschaltung → mobile Daten Automatischer Wechsel vom lokalen Modus zu Gladys Plus, ohne sichtbare Abmeldung
Rückkehr in den Vordergrund nach einer Nacht WebSocket-Wiederverbindung, aktualisierter Zustand, keine veralteten Daten werden als aktuell angezeigt
Benachrichtigung, App geschlossen Empfang, Öffnen auf dem richtigen Bildschirm, schnelle Aktion ausgeführt ohne Start der App
Eintritt und Austritt aus der Zone Präsenzereignis hochgeladen, einschließlich nach einem Netzwerkausfall
Berechtigung verweigert Die App bleibt vollständig nutzbar, mit einer Nachricht, die erklärt, was deaktiviert ist
Live-Kamera Abspielen, Vollbild, Rotation, sauberes Stoppen beim Wechsel in den Hintergrund
Dunkler Modus Konsistenz der Statusleiste, des Startbildschirms und der nicht invertierten Bereiche
Instanz offline Offline-Bildschirm, keine aggressive automatische Wiederverbindung, manuelle Wiederaufnahme möglich

Gemessene Ergebnisse — 30. August 2026

Auf Xiaomi 2412DPC0AG, Android 16 (API 36), WebView Chromium 151, gegen eine
reale Instanz in HTTP im lokalen Netzwerk:

Test Ergebnis
crypto.subtle im Kontext https://localhost verfügbar
RSA-OAEP 2048 / SHA-256 — Generierung 104 ms
ECDSA P-256 — Generierung, Signatur, Überprüfung < 1 ms
exportKey('jwk') (Schlüsselspeicherung) OK
PBKDF2 100 000 Iterationen 15 ms
AES-GCM 256, tagLength 128 < 1 ms
WebSocket zu HTTP-Instanz (Handshake) 44 ms
MSE — Hls.isSupported() verfügbar
Vollständiger Ablauf: Konfiguration → Login → Dashboards → Kameras funktionell

PBKDF2 ist dreimal schneller auf dem Telefon als auf einem Desktop-PC
(15 ms gegenüber 45 ms): Die Android-Implementierung ist hardwarebeschleunigt. Das
Risiko einer langsamen Entsperrung, das in Abschnitt 5.7 in Betracht gezogen wurde, besteht nicht.

Methodenlehre. Der anfängliche Validierungsprototype gab drei grüne Lichter und hat dennoch keine der beiden realen Blockaden erkannt,
die später auftraten (Mixed-Content bei HTTP-Anfragen, dann beim WebSocket). Er testete ein WebSocket von einer Seite http://localhost — also ohne
Mixed-Content — und gab keine API-Anfrage an die Instanz. Man muss Crypto, WebSocket und eine echte API-Anfrage in der endgültigen Schemakonfiguration validieren, niemals isoliert.

Minimales Testgeräte-Portfolio

  • Ein aktuelles Android-Gerät und ein älteres Android-Gerät (API 26 bis 28), für die Netzwerksicherheitskonfiguration.
  • Ein iPhone mit Notch und ein iPad, für sichere Bereiche und Tablet-Modus.
  • Eine lokale Gladys-Instanz unter http:// und eine mit Gladys Plus verknüpfte Instanz.

12. Außerhalb des Umfangs der v1

Diese Elemente werden explizit ausgeschlossen. Ihre Erwähnung verhindert, dass sie sich im Laufe der Zeit einschleichen.

  • Vollständiger Offline-Modus. Die App zeigt die zuletzt bekannten Daten an, spielt aber keine im Offline-Modus gesendeten Befehle nach. Nur Anwesenheitsereignisse werden in die Warteschlange gestellt.
  • Apple Watch- und Wear OS-Anwendung. Widgets und Systemverknüpfungen decken den größten Teil des Bedarfs zu geringeren Kosten ab.
  • Konfiguration komplexer Integrationen von mobilen Geräten aus: Zigbee-, Matter-, Z-Wave-Pairing. Abrufbar, aber über das Web konfiguriert.
  • Visuelles Redesign. Die App übernimmt die aktuelle Schnittstelle. Ein mobiles Redesign ist ein separates Projekt.
  • Unterstützung für Android 7 und ältere Versionen sowie für iOS 14 und ältere Versionen.
  • Reparatur des Web-Service-Workers. Das aktuelle Verhalten bleibt erhalten; die Verbesserung der PWA ist ein eigenständiges Thema.

Entscheidungen, die vor dem Start getroffen werden müssen

  1. Wird der Benachrichtigungsrelais über die bestehende Gladys Plus-Infrastruktur geleitet, oder ist ein dedizierter Dienst erforderlich? Dies beeinflusst das Los 3.
  2. Werden die Entwicklerkonten im Namen des Gladys-Projekts oder privat eröffnet? Dies hat dauerhafte Auswirkungen auf die Eigentumsverhältnisse der Dateien.
  3. Ist die App nur für Gladys Plus-Abonnenten für Benachrichtigungen reserviert, oder reicht der lokale Fallout für die an die Benutzer gegebene Zusicherung aus?
  4. Steht ein macOS-Gerät für die iOS-CI zur Verfügung, oder muss ein gehosteter Build-Service vorgesehen werden?

Spezifikation basierend auf dem Gladys 5.0.2-Repository: Frontend Preact / Vite (front/), Node 24-Server (server/, 39 Dienste, 25 API-Controller). Die zitierten Dateipfade entsprechen der tatsächlichen Verzeichnisstruktur des Repositories zum Zeitpunkt der Erstellung.

Hast du alles gelesen? :face_with_crossed_out_eyes:

Mist, ich bin bei „Drei Dinge, die die Spezifikation nicht vorgesehen hat“ rausgefallen, ich konnte nicht mehr, tut mir leid @Will_71 :face_with_peeking_eye:

Zusammengefasst, wofür soll eine native App gut sein?
Ich finde das WPA-System wirklich super, es funktioniert immer, ist stabil und sicher.
Mit mobilen Apps sind es zwei weitere Plattformen, die verwaltet und gepflegt werden müssen, in Bezug auf Kompatibilität mit Gladys, OS-Updates, Sicherheit (wir werden versuchen, es nicht wie die Steuerbehörde zu machen…), usw.
Also ja, warum?

PS: Ich habe das Video noch nicht gesehen, vielleicht finde ich dort Antworten…

Das war nur ein Test, um zu sehen, was Claude kann, und in 15 Minuten hatte ich die Spezifikation und eine funktionierende App.

Ich sage nicht, dass die PWA-App nicht funktioniert, im Gegenteil, ich nutze sie jeden Tag.
Hier hat Claude nicht drei verschiedene Systeme erstellt, sondern sie teilt sich das gleiche Frontend, sodass Änderungen dort für alle drei verfügbar sind.

Der Vorteil der App ist, dass sie die Geolokalisierung einer Person (für die Anwesenheit in einem Bereich) nativ über das Smartphone verwalten kann.
Und ich kann mir gut vorstellen, die App später zu nutzen, um Matter-Geräte über Bluetooth und die Telefonkamera zu koppeln.