Skip to content

Status und Requests

Diese Seite beschreibt, wie die App Requests aufbaut, ausführt und Antworten verarbeitet.

WidgetStatusConfig

Feld Typ Default Bedeutung
request EndpointRequestConfig? null direkter Status-Request
sharedRequestId String? null Referenz auf globalen Sammelrequest
responseKind ResponseKind auto erwartete Antwortart

Wichtige Regel:

  • genau eines von request oder sharedRequestId muss gesetzt sein
  • wenn ein Widget gar keinen Status braucht, darf status nur bei mode = "icon_text" oder mode = "shutter" beziehungsweise mode = "cover" komplett fehlen

EndpointRequestConfig

Feld Typ Default Bedeutung
url String kein Default Zieladresse
method EndpointRequestMethod? null optionale HTTP-Methode
headers Map<String, String> {} Request-Header
jsonBody JsonElement? null optionaler JSON-Body
timeoutMs Long? null überschreibt globales Timeout

Request-Aufbau

Zur Laufzeit erzeugt das Repository aus RequestDefaultsConfig und EndpointRequestConfig einen ResolvedRequest.

Merge-Regeln:

  • URL kommt immer aus dem konkreten Request
  • Header werden aus Defaults und Request zusammengeführt
  • widget-spezifische Header überschreiben Default-Header mit gleichem Key
  • timeoutMs des Requests überschreibt das Default-Timeout
  • wenn kein Accept gesetzt ist, setzt die App application/json, image/png, image/jpeg

HTTP-Methode

Unterstützte Werte für method:

  • get
  • post
  • put
  • patch
  • delete

Wenn method nicht gesetzt ist, verwendet die App automatisch:

  • GET, wenn jsonBody = null
  • POST, wenn jsonBody gesetzt ist

Wichtige Regel:

  • GET wird ohne JSON-Body ausgeführt
  • für Requests mit jsonBody solltest du POST, PUT, PATCH oder DELETE verwenden

Bedingte GET-Requests mit Last-Modified

Fuer Status-Requests mit GET nutzt die App automatisch Conditional Requests, wenn der Server das unterstuetzt:

  1. ein erfolgreicher Response liefert z. B. Last-Modified: Sun, 23 Mar 2026 09:55:00 GMT
  2. die App merkt sich diesen Wert waehrend der laufenden Sitzung
  3. beim naechsten Refresh sendet sie If-Modified-Since mit
  4. bei 304 Not Modified wird kein Body geladen und der bisherige Snapshot weiterverwendet

Wichtig:

  • das Verhalten gilt fuer Widget-Statusrequests, nicht fuer Aktionsrequests
  • gespeichert wird der Wert nur im laufenden App-Prozess
  • ohne Last-Modified-Header faellt die App automatisch auf normale GET-Requests zurueck
  • fuer haeufig gepollte Endpoints wie Charts, Kameras oder grosse Statuspayloads spart das Netzwerk und Resolver-Arbeit

ResponseKind

Wert Bedeutung
auto Antwort wird automatisch erkannt
json es wird JSON erwartet
text es wird ein einzelner Text-, Zahl- oder Boolean-Wert erwartet
jpg es wird ein JPG/JPEG erwartet
png es wird ein PNG erwartet

Automatische Erkennung:

  • image/png im Content-Type reicht aus
  • image/jpeg oder image/jpg im Content-Type reichen ebenfalls aus
  • alternativ prüft die App auch die PNG-Signatur der ersten Bytes
  • für JPG prüft die App zusätzlich die JPEG-Signatur
  • alles andere wird als JSON oder roher Text interpretiert

Hinweis zu text:

  • sinnvoll für Endpoints, die nur on, off, 23.5 oder true liefern
  • der Wert steht als primitiver Root-Wert zur Verfügung
  • für title.value oder value.value kannst du deshalb bei mode = "path" den Pfad $ verwenden

Beispiel für einen Text-Endpoint:

{
  "content": {
    "mode": "icon_text",
    "title": {
      "mode": "static",
      "value": "API-Status"
    },
    "value": {
      "mode": "path",
      "value": "$",
      "fallback": "unbekannt"
    }
  },
  "status": {
    "request": {
      "url": "https://example.org/api/healthz"
    },
    "responseKind": "text"
  }
}

Ergebnisobjekt EndpointExecutionResult

Zur Laufzeit liefert das Netzwerkmodul:

Feld Typ Bedeutung
statusCode Int HTTP-Statuscode
contentType String? Server-Content-Type
jsonPayload JsonElement? JSON- oder Textnutzlast
imageBytes ByteArray? Bilddaten, z. B. PNG oder JPG

Shared-Status-Requests

defaults.sharedStatusRequests erlaubt es, mehrere Widgets an dieselbe Statusantwort zu hängen.

Beispiel:

{
  "defaults": {
    "sharedStatusRequests": [
      {
        "id": "living-room-light-state",
        "request": {
          "url": "http://192.168.1.20/api/lights/living-room"
        }
      }
    ]
  },
  "widgets": [
    {
      "id": "light",
      "status": {
        "sharedRequestId": "living-room-light-state"
      }
    },
    {
      "id": "dimmer",
      "status": {
        "sharedRequestId": "living-room-light-state"
      }
    }
  ]
}

Laufzeitverhalten:

  • pro Refresh werden nur benötigte Shared-Requests ausgeführt
  • jeder benötigte Shared-Request läuft exakt einmal
  • alle Widgets lesen danach unterschiedliche Werte aus derselben JSON-Antwort

Fehlerverhalten

Wenn ein Widget nicht aktualisiert werden kann:

  • das Widget bleibt sichtbar
  • hasError = true
  • die Karte wird visuell ausgegraut
  • der bisherige Snapshot bleibt erhalten

URL-Regeln

Zulässig:

  • https://example.org/api/state
  • http://192.168.1.20/api/state

Nicht zulässig:

  • http://example.org/api/state
  • http://10.0.0.5/api/state
  • http://localhost/api/state
  • ftp://...

Die Logik sitzt in UrlPolicy.isAllowed(...).

Beispiel für einen Request mit Headern und Body

{
  "request": {
    "url": "https://example.org/api/energy/current",
    "method": "post",
    "headers": {
      "X-Api-Key": "demo"
    },
    "jsonBody": {
      "scope": "today"
    },
    "timeoutMs": 8000
  }
}

Empfehlung für Response-Design

Wenn du die API selbst kontrollierst, sind diese Muster besonders gut nutzbar:

  • flache JSONs für kleine Widgets
  • konsistente Namensgebung für gemeinsam genutzte Statuspayloads
  • Arrays aus Objekten für Select-Optionen
  • getrennte Arrays pro Kurve für Chart-Widgets, z. B. pv.history, house.samples, battery.points
  • separate PNG-Endpunkte für Kamerabilder

Siehe auch