Skip to content

Architektur

Die App ist bewusst in wenige, klar getrennte Schichten geschnitten. Die Konfiguration ist datengetrieben, die Laufzeitlogik sitzt im ViewModel, und das Rendering in Compose verarbeitet bereits aufbereitete Snapshots.

Schichtenmodell

1. Konfigurationsmodell

Datei:

  • app/src/main/java/de/ringelbaer/jsonsmarthomedashboard/data/model/DashboardConfig.kt

Verantwortung:

  • definiert die serialisierbaren JSON-Strukturen
  • enthält die zentralen Enums für Render-Modi, Aktionen und Response-Arten
  • beschreibt Dashboard, Widgets, Requests, Slider-, Select- und Rollladenkonfiguration

2. Laufzeitmodell

Datei:

  • app/src/main/java/de/ringelbaer/jsonsmarthomedashboard/data/model/RuntimeModels.kt

Verantwortung:

  • wandelt Konfiguration in renderbare Laufzeitstrukturen um
  • erzeugt ResolvedDashboardTab, ResolvedDashboardGroup und WidgetSnapshot
  • bestimmt die tatsächlich sichtbaren Content-Blöcke pro Tab

3. Persistenz und Repository

Dateien:

  • data/config/AppPreferences.kt
  • data/config/ConfigCache.kt
  • data/config/ThemePreferences.kt
  • data/repository/DashboardRepository.kt

Verantwortung:

  • speichert URL, gewählte Profil-ID, Debug-Option und Theme-Einstellungen
  • cached die zuletzt geladene Dashboard-JSON lokal
  • verarbeitet den Import von Farb-Template-Dateien
  • validiert Konfigurationen
  • lädt direkte Configs, index.json-Profile und lokale Dateien
  • führt Status- und Action-Requests aus

4. Netzwerk

Datei:

  • data/network/SmartHomeHttpClient.kt

Verantwortung:

  • führt konfigurierte HTTP-Requests aus
  • verwendet ohne explizite Methode GET ohne Body und POST mit Body
  • unterscheidet JSON und PNG automatisch
  • gibt ein neutrales EndpointExecutionResult zurück

5. Resolver und Utilities

Dateien:

  • util/WidgetPresentationResolver.kt
  • util/JsonPathResolver.kt
  • util/JsonTemplateResolver.kt
  • util/JsonPathUpdater.kt
  • util/RequestValueBindingResolver.kt
  • util/UrlPolicy.kt

Verantwortung:

  • lesen Werte aus JSON-Antworten
  • rendern Texttemplates
  • aktualisieren JSON-Strukturen für optimistische Updates
  • schreiben Aktionswerte in JSON-Body, Header oder Query-Parameter
  • prüfen erlaubte URLs

6. ViewModel

Datei:

  • ui/DashboardViewModel.kt

Verantwortung:

  • Bootstrapping
  • Refresh aller Widgets
  • Einstellungszustand für Konfiguration, Farben und Debug
  • Dialogzustände für Slider, Select, Rollladen und Profilwahl
  • Tab-Wechsel
  • optimistic updates für Slider und Select
  • User-Messages und Browser-Öffnung

7. Compose-UI

Dateien:

  • ui/DashboardScreen.kt
  • ui/components/DashboardGrid.kt
  • ui/components/SettingsScreen.kt
  • ui/components/ConfigProfilePickerDialog.kt
  • ui/components/SliderActionDialog.kt
  • ui/components/SelectActionDialog.kt
  • ui/components/ShutterControlDialog.kt

Verantwortung:

  • Dashboard-Layout und kompakte Kopfzeile mit Zeitstempel sowie Online-/Fehlerstatus
  • Raster-Packing und Widget-Rendering
  • ganzseitige Einstellungsseite mit Reitern für Konfiguration, Farben und Debug
  • modale Dialoge für alle interaktiven Widget-Typen

Laufzeitfluss

Konfiguration laden

  1. Einstellungsseite oder Bootstrapping liefert eine Quelle
  2. Repository lädt JSON
  3. Repository validiert Struktur und URLs
  4. ViewModel ruft applyConfig(...) auf
  5. Basissnapshots werden für alle Widgets erzeugt
  6. refreshStatuses() lädt alle Statusdaten
  7. WidgetPresentationResolver baut daraus renderbare Widget-Snapshots

Status aktualisieren

  1. ViewModel markiert Widgets als isLoading
  2. Repository führt Shared-Status-Requests zuerst aus
  3. direkte Widget-Requests laufen parallel
  4. Ergebnisse werden per WidgetPresentationResolver.resolve(...) in Snapshots überführt
  5. Fehlerhafte Widgets bleiben sichtbar, werden aber ausgegraut
  6. lastUpdatedAt und failureCount werden global gesetzt

Aktion ausführen

  1. Tap auf Widget oder Dialog-Bestätigung
  2. ViewModel erstellt den finalen Request
  3. Binding-Resolver schreiben Werte in Request
  4. Repository führt Request aus
  5. bei aktivierter Debug-Option: Config vor dem Refresh erneut von der URL laden
  6. bei refreshAfterAction = true: nur betroffenes Widget neu laden
  7. bei refreshAfterAction = false: optimistisches Update im Snapshot

Theme und Farb-Templates

  1. AppPreferences speichert Theme-Modus und Custom-Farben
  2. DashboardViewModel stellt Theme-Zustand für die UI bereit
  3. MainActivity übergibt diese Werte an JsonSmartHomeDashboardTheme(...)
  4. im Reiter Farben können Farben manuell oder per JSON-Template importiert werden

Warum WidgetSnapshot zentral ist

Die UI rendert nicht direkt aus Roh-JSON, sondern aus WidgetSnapshot. Das reduziert Komplexität an drei Stellen:

  • Rendering muss nur eine kleine, UI-freundliche Struktur verstehen
  • Fehler- und Ladezustände liegen am selben Ort wie Titel, Value und Bild
  • optimistische Updates können auf Snapshot-Ebene konsistent angewendet werden

Sortierung und Auflösung

  • Tabs werden nach dashboard.tabs[].order, danach nach Titel und ID sortiert.
  • Inhalte innerhalb eines Tabs werden nach order sortiert.
  • Bei gleichen order-Werten bleibt die Deklarationsreihenfolge erhalten.
  • Gruppen werden als eigene Content-Blöcke in den Tabfluss eingefügt.
  • Vollbreiten-Widgets (full/n) erzwingen eine neu Zeile und verhindern Auffüllen von Lücken davor.

Mehr dazu auf Tabs, Gruppen und Grid.

Testbarkeit

Die App ist für JVM-Tests vorbereitet:

  • DashboardDataSource abstrahiert Repository-Zugriffe
  • DashboardStrings abstrahiert UI-Texte
  • DashboardViewModel besitzt einen injizierbaren Test-Konstruktor

Wichtige Testdateien:

  • DashboardTabsTest.kt
  • DashboardViewModelActionTest.kt
  • WidgetPresentationResolverTest.kt
  • UrlPolicyTest.kt

Erweiterungspunkte

Neu Features lassen sich am saubersten an diesen Punkten ergänzen:

  • neues Widget-Schema in DashboardConfig.kt
  • Snapshot-Ableitung in WidgetPresentationResolver.kt
  • Dialog und Bedienlogik in DashboardViewModel.kt
  • Rendering in DashboardGrid.kt
  • Validierung in DashboardRepository.kt