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,ResolvedDashboardGroupundWidgetSnapshot - bestimmt die tatsächlich sichtbaren Content-Blöcke pro Tab
3. Persistenz und Repository¶
Dateien:
data/config/AppPreferences.ktdata/config/ConfigCache.ktdata/config/ThemePreferences.ktdata/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
GETohne Body undPOSTmit Body - unterscheidet JSON und PNG automatisch
- gibt ein neutrales
EndpointExecutionResultzurück
5. Resolver und Utilities¶
Dateien:
util/WidgetPresentationResolver.ktutil/JsonPathResolver.ktutil/JsonTemplateResolver.ktutil/JsonPathUpdater.ktutil/RequestValueBindingResolver.ktutil/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.ktui/components/DashboardGrid.ktui/components/SettingsScreen.ktui/components/ConfigProfilePickerDialog.ktui/components/SliderActionDialog.ktui/components/SelectActionDialog.ktui/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¶
- Einstellungsseite oder Bootstrapping liefert eine Quelle
- Repository lädt JSON
- Repository validiert Struktur und URLs
- ViewModel ruft
applyConfig(...)auf - Basissnapshots werden für alle Widgets erzeugt
refreshStatuses()lädt alle StatusdatenWidgetPresentationResolverbaut daraus renderbare Widget-Snapshots
Status aktualisieren¶
- ViewModel markiert Widgets als
isLoading - Repository führt Shared-Status-Requests zuerst aus
- direkte Widget-Requests laufen parallel
- Ergebnisse werden per
WidgetPresentationResolver.resolve(...)in Snapshots überführt - Fehlerhafte Widgets bleiben sichtbar, werden aber ausgegraut
lastUpdatedAtundfailureCountwerden global gesetzt
Aktion ausführen¶
- Tap auf Widget oder Dialog-Bestätigung
- ViewModel erstellt den finalen Request
- Binding-Resolver schreiben Werte in Request
- Repository führt Request aus
- bei aktivierter Debug-Option: Config vor dem Refresh erneut von der URL laden
- bei
refreshAfterAction = true: nur betroffenes Widget neu laden - bei
refreshAfterAction = false: optimistisches Update im Snapshot
Theme und Farb-Templates¶
AppPreferencesspeichert Theme-Modus und Custom-FarbenDashboardViewModelstellt Theme-Zustand für die UI bereitMainActivityübergibt diese Werte anJsonSmartHomeDashboardTheme(...)- im Reiter
Farbenkö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
ordersortiert. - 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:
DashboardDataSourceabstrahiert Repository-ZugriffeDashboardStringsabstrahiert UI-TexteDashboardViewModelbesitzt einen injizierbaren Test-Konstruktor
Wichtige Testdateien:
DashboardTabsTest.ktDashboardViewModelActionTest.ktWidgetPresentationResolverTest.ktUrlPolicyTest.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