Nachrichten Server → Client

Nachrichtenformat

Jede Nachricht ist ein JSON-Objekt mit genau einem Schlüssel der obersten Ebene. Dieser Schlüssel kodiert den Ereignistyp und kann eine lesbare, durch einen Bindestrich getrennte Beschreibung enthalten:

"aa-dump all data"   →  Ereignistyp = erste 2 Zeichen = "aa"
"ea"                 →  Ereignistyp = "ea"
"d"                  →  Ereignistyp = "d" (einzelnes Zeichen)

Clients müssen die ersten 1 oder 2 Zeichen des Schlüssels abgleichen, nicht die vollständige Zeichenkette, da sich der Beschreibungsteil ändern kann.

Alle vom Server gesendeten Ereignisschlüssel sind klein geschrieben (z. B. d, ea, ap, gc).


Übersicht der Ereignistypen

SchlüsselBeschreibung
dVollständiger initialer Dump (Antwort auf DU)
aaVollständiger Dump eines Moduls (Modul geladen/neu geladen)
apVollständiger Ersatz einer Property
eaUpdate aller Elementwerte einer Property
eeUpdate eines einzelnen Elementwerts
evUpdate eines Elementwerts mit seinen Einschränkungen
elLokale LOV eines Elements geändert
psUpdate von Status und Enabled-Zustand einer Property
dmModul gelöscht
dpProperty gelöscht
gcGrid-Zeile erstellt
guGrid-Zeile aktualisiert
gdGrid-Zeile gelöscht
grGrid-Reset (alle Zeilen gelöscht)
lcLOV erstellt oder zusammengeführt
luLOV ersetzt
ldLOV gelöscht
fsProfil gespeichert
flProfil geladen
fcProfil geändert (nicht gespeicherte Änderungen)
lLog-Eintrag
ucUpdate der Controller-Daten
afDatei hinzugefügt
dfDatei gelöscht
adVerzeichnis hinzugefügt
ddVerzeichnis gelöscht
qaAntwort auf eine Modul-Anfrage (Inter-Modul-Mechanismus, nicht für Clients gedacht)
xxKeepalive-Pong

d — Vollständiger initialer Dump

Wird als Antwort auf eine DU-Anfrage gesendet. Enthält alles: alle Module, Logs, Dateilisten und Controller-Daten. Die Details der Schlüssel von controllerdata und files stehen in Controller, GlobalDatastore und Dateien; das Beispiel unten zeigt nur einige davon.

{
  "d": {
    "grant-client": "1",
    "grant-server": "0",
    "serverlng": "en",
    "m": {
      "Focus": {
        "infos": { "modulelabel": "Focus", "description": "..." },
        "f": { "name": "default", "changed": false },
        "l": {
          "myLov": { "label": "My LOV", "type": "string", "values": { "a": "Option A" } }
        },
        "p": {
          "parameters": {
            "label": "Parameters",
            "order": "10",
            "level1": "Focus",
            "level2": "Config",
            "status": 0,
            "permission": 2,
            "enabled": true,
            "showElts": true,
            "hasGrid": false,
            "hasGraph": false,
            "rule": 0,
            "badge": false,
            "preicon1": "", "preicon2": "", "posticon1": "", "posticon2": "",
            "hasprofile": true,
            "freevalue": "",
            "e": {
              "iterations": {
                "type": "int",
                "label": "Iterations",
                "order": "10",
                "hint": "",
                "autoupdate": false,
                "badge": false,
                "directedit": false,
                "value": 5,
                "min": 1,
                "max": 20,
                "step": 1,
                "format": "",
                "slider": 0
              }
            }
          }
        }
      }
    },
    "files": {
      "folders": ["", "/Allsky", "/Allsky/archives"],
      "files": ["/image.jpeg"],
      "selectedfolder": "/home/user/ostmedia"
    },
    "logs": [
      { "d": "2024-01-01T12:00:00.000", "c": "Focus", "t": "Module loaded", "l": 1 }
    ],
    "controllerdata": {
      "profiles": {
        "Focus": ["default", "test"]
      },
      "currentconf": "default"
    },
    "lovs": {
      "sharedLov": { "label": "Shared LOV", "type": "string", "values": {} }
    }
  }
}
Abkürzungen im Wire-Format

m = modules, p = properties, e = elements, l = globallovs, f = profile. Diese Abkürzungen bei der internen Speicherung auf ihre vollständigen Namen normalisieren.


aa — Vollständiger Dump eines Moduls (Modul geladen/neu geladen)

Wird gesendet, wenn ein Modul zur Laufzeit geladen oder neu geladen wird. Enthält den vollständigen Zustand des Moduls. Der Schlüssel beginnt mit "aa".

{
  "aa-dump all data": {
    "Guider": {
      "infos": { "modulelabel": "Guider", "description": "..." },
      "f": { "name": "default", "changed": false },
      "l": {},
      "p": {
        "status": { ... }
      }
    }
  }
}

ap — Vollständiger Ersatz einer Property

Ersetzt eine oder mehrere Properties vollständig. Der Schlüssel beginnt mit "ap".

{
  "ap-dump all property data": {
    "Focus": {
      "p": {
        "parameters": {
          "label": "Parameters",
          "status": 0,
          "permission": 2,
          "enabled": true,
          "showElts": true,
          "hasGrid": false,
          "hasGraph": false,
          "rule": 0,
          "e": {
            "iterations": {
              "type": "int",
              "value": 7,
              "min": 1, "max": 20, "step": 1,
              "label": "Iterations", "order": "10", "hint": "",
              "autoupdate": false, "badge": false, "directedit": false,
              "format": "", "slider": 0
            }
          }
        }
      }
    }
  }
}

ea — Update aller Elementwerte (Property-Ebene)

Die häufigste Update-Nachricht. Sendet die aktuellen Werte aller Elemente einer Property. Enthält ausschließlich Werte — keine Metadaten. Der Schlüssel ist genau "ea".

{
  "ea": {
    "Allsky": {
      "p": {
        "coming": {
          "e": {
            "sunrise": { "hh": 7, "mm": 6, "ss": 51 },
            "sunset":  { "hh": 21, "mm": 15, "ss": 1 }
          }
        }
      }
    }
  }
}

Bei einfachen Typen (int, float, bool, string, light) ist der Wert ein Skalar:

{
  "ea": {
    "Focus": {
      "p": {
        "parameters": {
          "e": {
            "iterations": 5,
            "exposure": 3.5,
            "active": true
          }
        }
      }
    }
  }
}

ee — Update eines einzelnen Elementwerts

Aktualisiert den Wert eines einzelnen Elements. Gleiches Format wie ea, aber für ein einzelnes Element. Der Schlüssel ist genau "ee".

{
  "ee": {
    "Focus": {
      "p": {
        "parameters": {
          "e": {
            "iterations": 7
          }
        }
      }
    }
  }
}
ee kommt nur vom Backend aus

ee wird ausschließlich auf Initiative des Backends selbst gesendet (z. B. eine geräteseitige Wertänderung, internes Polling) — nie als direkte Antwort auf einen Client-Schreibvorgang. Jede Antwort auf ein clientseitig ausgelöstes SV oder SA ist immer ein ea der gesamten Property, unabhängig davon, wie viele Elemente sich tatsächlich geändert haben.


ev — Update eines Elements mit Metadaten

Aktualisiert den Wert eines Elements sowie seine Einschränkungen (min, max, format). Der Schlüssel beginnt mit "ev".

{
  "ev-set one element value/min/max/format ": {
    "Focus": {
      "p": {
        "parameters": {
          "e": {
            "temperature": {
              "value": 12.5,
              "min": -40.0,
              "max": 80.0,
              "format": "%.1f"
            }
          }
        }
      }
    }
  }
}

el — Lokale LOV geändert

Wird gesendet, wenn die eigene lokale Liste von Werten (listOfValues) eines Elements auf Initiative des Servers hinzugefügt, aktualisiert oder gelöscht wird — z. B. wenn die Filterrad-Slotnamen (neu) vom Gerät gelesen werden. Der Schlüssel beginnt mit "el". Gleiches Payload-Format wie ev (aktueller Wert, Einschränkungen und listOfValues sind alle enthalten), aber im Gegensatz zu ev markiert dies niemals das Profil einer hasprofile-Property als geändert (fc): eine lokale LOV ist dekorativ, kein gespeicherter Profilwert.

{
  "el-element's own local lov changed": {
    "Sequencer": {
      "p": {
        "sequence": {
          "e": {
            "filter": {
              "value": 1,
              "listOfValues": { "1": "Luminance", "2": "Red", "3": "Green", "4": "Blue" }
            }
          }
        }
      }
    }
  }
}

ps — Update des Status einer Property

Aktualisiert ausschließlich die Felder status und enabled einer Property. Der Schlüssel beginnt mit "ps".

{
  "ps-only property state": {
    "Focus": {
      "p": {
        "autofocus": {
          "status": 2,
          "enabled": true
        }
      }
    }
  }
}

Statuswerte: 0 Ruhe · 1 OK · 2 Beschäftigt · 3 Fehler


dm — Modul gelöscht

Wird gesendet, wenn ein Modul entladen wird. Der Client muss alle Daten dieses Moduls entfernen. Der Schlüssel beginnt mit "dm".

{
  "dm-delete/remove module": {
    "Focus": {}
  }
}

dp — Property gelöscht

Wird gesendet, wenn eine Property aus einem Modul entfernt wird. Der Schlüssel beginnt mit "dp".

{
  "dp-delete/remove property": {
    "Focus": {
      "p": {
        "oldProperty": ""
      }
    }
  }
}

gc — Grid-Zeile erstellt

Wird gesendet, nachdem eine neue Zeile in einer Grid-Property hinzugefügt wurde. Der Schlüssel beginnt mit "gc".

{
  "gc-grid new line ": {
    "Sequencer": {
      "p": {
        "sequence": {
          "i": 3,
          "values": {
            "target": "M31",
            "exposure": 120,
            "count": 10
          }
        }
      }
    }
  }
}

i ist der Index der neu erstellten Zeile. values ist ein Objekt, indexiert nach Elementname.


gu — Grid-Zeile aktualisiert

Wird gesendet, wenn eine bestehende Grid-Zeile geändert wird. Der Schlüssel beginnt mit "gu".

{
  "gu-grid update line": {
    "Sequencer": {
      "p": {
        "sequence": {
          "i": 1,
          "values": {
            "target": "M42",
            "exposure": 60,
            "count": 20
          }
        }
      }
    }
  }
}

gd — Grid-Zeile gelöscht

Wird gesendet, wenn eine Grid-Zeile entfernt wird. Der Schlüssel beginnt mit "gd".

{
  "gd-grid delete line": {
    "Sequencer": {
      "p": {
        "sequence": {
          "i": 0
        }
      }
    }
  }
}

i ist der (nullbasierte) Index der gelöschten Zeile. Nachfolgende Zeilen rücken um eine Position vor.


gr — Grid-Reset

Löscht die gesamte Grid einer Property. Der Schlüssel beginnt mit "gr".

{
  "gr-grid reset": {
    "Sequencer": {
      "p": {
        "sequence": {}
      }
    }
  }
}

lc / lu / ld — LOV-Ereignisse

lc — LOV erstellt/zusammengeführt (Schlüssel beginnt mit "lc"):

Modul-LOV:

{
  "lc-lov create": {
    "Focus": {
      "l": {
        "myLov": {
          "label": "My LOV",
          "type": "string",
          "values": { "a": "Option A", "b": "Option B" }
        }
      }
    }
  }
}

Controller-LOV (ohne Modul-Struktur, verwendet den Schlüssel lovs):

{
  "lc-lov create": {
    "lovs": {
      "sharedLov": {
        "label": "Shared LOV",
        "type": "string",
        "values": { "x": "Value X" }
      }
    }
  }
}

lu — LOV ersetzt (gleiches Format wie lc, vollständiger Ersatz).

ld — LOV gelöscht (Schlüssel beginnt mit "ld"):

{
  "ld-lov delete": {
    "lovs": {
      "sharedLov": {}
    }
  }
}

fs / fl / fc — Profil-Ereignisse

Werden gesendet, wenn ein Profil gespeichert, geladen oder geändert wird.

{
  "fs-profile saved": {
    "Focus": {
      "f": { "name": "myProfile", "changed": false }
    }
  }
}
SchlüsselpräfixEreignis
fsProfil gespeichert
flProfil geladen
fcProfil mit nicht gespeicherten Änderungen

l — Log-Eintrag

Wird für jede vom Server erzeugte Log-Nachricht gesendet. Der Schlüssel ist genau "l".

{
  "l": {
    "d": "2024-01-01T12:00:00.000",
    "c": "Focus",
    "t": "Autofocus completed successfully",
    "l": 1
  }
}
FeldBeschreibung
dISO-8601-Datum/Uhrzeit mit Millisekunden
cKontext/Quelle (Modulname oder "WS")
tNachrichtentext (übersetzt in die Sprache des Clients)
lStufe: 0 Debug · 1 Info · 2 Warnung · 3 Fehler · 4 Kritisch

uc — Update der Controller-Daten

Wird gesendet, wenn sich globale Controller-Daten ändern: Profile, Konfigurationen, aktive INDI-Treiber, Dateilisten (files, folders), Systemlast (systemwatcher, standardmäßig alle 10 s). Der Schlüssel beginnt mit "uc". Die möglichen Schlüssel sind in Controller, GlobalDatastore und Dateien beschrieben.

{
  "uc-update controller data": {
    "profiles": {
      "Focus": ["default", "highres"],
      "Guider": ["default"]
    }
  }
}

Der Payload ist {"Schlüssel": Wert}, wobei Wert eine Zeichenkette, ein Array oder ein Objekt sein kann. Jede Nachricht trägt einen einzigen Schlüssel, und ihr Wert ersetzt den alten vollständig.


af / df / ad / dd — Datei- und Ordnerereignisse

Wird an alle Clients gesendet, wenn sich der Inhalt des überwachten Dateiordners ändert (siehe Dateisystem). Anders als bei anderen Nachrichten ist der Wert eine reine Zeichenkette: der betroffene Pfad, nicht in ein Objekt oder Array verpackt — der Server meldet immer nur einen Pfad pro Nachricht.

{ "af": "/Allsky/image_001.jpeg" }
{ "df": "/Allsky/old_image.jpeg" }
SchlüsselBedeutung
afDatei zum ausgewählten Ordner hinzugefügt
dfDatei aus dem ausgewählten Ordner entfernt
adUnterordner irgendwo im Webroot hinzugefügt
ddUnterordner irgendwo im Webroot entfernt

af/df betreffen nur den aktuell über FS ausgewählten Ordner — für den Inhalt anderer Ordner wird nichts gemeldet. ad/dd sind global: sie lösen für den gesamten Webroot-Baum aus, unabhängig von der aktuellen Auswahl. Siehe Dateisystem für das Gesamtbild.


qa — Antwort auf eine Modul-Anfrage

Inter-Modul-Kommunikation, keine Client-Funktion

Siehe den Hinweis zu QY: dieser Mechanismus existiert in erster Linie dafür, dass ein Modul ein anderes intern abfragt. Hier der Vollständigkeit halber dokumentiert, nicht als Client-Funktion.

Antwort auf eine QY-Anfrage. QY/qa ist ein generischer, pro Modul definierter Anfrage-/Antwort-Mechanismus: ein Client (oder intern ein anderes Modul) stellt einem Modul eine benannte Frage mit Parametern und erhält eine benannte Antwort zurück. Welche Anfragenamen ein Modul tatsächlich versteht und wie seine Antwort aussieht, entscheidet allein das Modul — das Protokoll definiert nur die Hülle, nicht den Inhalt.

{
  "qa-query answer": {
    "Sequencer": {
      "q": {
        "query": "profileduration",
        "result": { "seconds": 3600 }
      }
    }
  }
}

result ist das, was das Modul für diese Anfrage zurückgegeben hat — seine Form hängt vollständig von query ab. Ein Modul ohne passenden Fall für den angefragten query-Namen liefert stillschweigend ein leeres result.

Noch nicht viel zu sehen

Heute implementiert nur das Modul Sequencer eine echte Antwort (profileduration), intern genutzt vom Planner-Modul statt von einem dokumentierten Client-Workflow. Kein anderes ausgeliefertes Modul antwortet bislang sinnvoll auf irgendeinen Anfragenamen — der Mechanismus existiert und ist vollständig verdrahtet, aber es gibt derzeit praktisch wenig abzufragen.


xx — Keepalive-Pong

Wird vom Server als Antwort auf einen Client-Heartbeat (XX) gesendet. Kein nützlicher Payload; Clients können ihn ignorieren.

{ "xx": {} }