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

**URL:** <https://community.gladysassistant.com/t/une-application-mobile-android-ios-pour-gladys-approche-et-premiers-resultats/10769>\
**Category:** Développement\
**Created:** [30. August 2026 um 14:49 UTC](https://community.gladysassistant.com/t/une-application-mobile-android-ios-pour-gladys-approche-et-premiers-resultats/10769 "2026-08-30T14:49:39Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Will\_71](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/will_71/32/9582_2.png) [@Will\_71](https://community.gladysassistant.com/u/Will_71)\
**Post date:** [30. August 2026 um 14:49 UTC](https://community.gladysassistant.com/t/une-application-mobile-android-ios-pour-gladys-approche-et-premiers-resultats/10769/1 "2026-08-30T14:49:40Z")

</div>

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… 🤩

 ![image](https://gladys-assistant-community.b-cdn.net/uploads/default/original/3X/c/8/c8f91b1405b715a69aff64d238d9fd9069256fcc.jpeg)

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.

```auto
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=«&nbsp;📄 Vollständige technische Spezifikation (aufklappen)&nbsp;»]

**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` | ✅ **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` | ✅ **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.

```auto
┌─────────────────────────────────────┐
│ 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/`:

```auto
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 ✅ **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.

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

```auto
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?

* * *

_Speziﬁkation 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._

---

<div class="post-metadata">

**Author:** ![Jluc](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/jluc/32/12397_2.png) [@Jluc](https://community.gladysassistant.com/u/Jluc)\
**Post date:** [30. August 2026 um 15:38 UTC](https://community.gladysassistant.com/t/une-application-mobile-android-ios-pour-gladys-approche-et-premiers-resultats/10769/2 "2026-08-30T15:38:14Z")

</div>

Hast du alles gelesen? 😵

---

<div class="post-metadata">

**Author:** ![mutmut](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/mutmut/32/14064_2.png) [@mutmut](https://community.gladysassistant.com/u/mutmut)\
**Post date:** [30. August 2026 um 19:22 UTC](https://community.gladysassistant.com/t/une-application-mobile-android-ios-pour-gladys-approche-et-premiers-resultats/10769/3 "2026-08-30T19:22:07Z")

</div>

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

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…

---

<div class="post-metadata">

**Author:** ![Will\_71](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/will_71/32/9582_2.png) [@Will\_71](https://community.gladysassistant.com/u/Will_71)\
**Post date:** [30. August 2026 um 19:43 UTC](https://community.gladysassistant.com/t/une-application-mobile-android-ios-pour-gladys-approche-et-premiers-resultats/10769/4 "2026-08-30T19:43:56Z")

</div>

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.
