Server → Client Messages
Message format
Every message is a JSON object with one top-level key. That key encodes the event type and may include a human-readable description separated by a dash:
"aa-dump all data" → event type = first 2 characters = "aa"
"ea" → event type = "ea"
"d" → event type = "d" (single character)Clients should match on the first 1 or 2 characters of the key, not the full string, since the description part can change.
All server-sent event keys are lowercase (e.g. d, ea, ap, gc).
Event type index
| Key | Description |
|---|---|
d | Full initial dump (response to DU) |
aa | Full module dump (module loaded/reloaded) |
ap | Full property replacement |
ea | Set all element values for a property |
ee | Set one element value |
ev | Set element value with updated constraints |
el | Element’s own local LOV changed |
ps | Property status and enabled update |
dm | Module deleted |
dp | Property deleted |
de | Element deleted |
gc | Grid line created |
gu | Grid line updated |
gd | Grid line deleted |
gr | Grid reset (all rows cleared) |
lc | LOV created or merged |
lu | LOV replaced |
ld | LOV deleted |
fs | Profile saved |
fl | Profile loaded |
fc | Profile has unsaved changes |
l | Log entry |
uc | Controller data update |
af | File added |
df | File deleted |
ad | Directory added |
dd | Directory deleted |
qa | Answer to a module query (inter-module mechanism, not client-facing) |
xx | Keepalive pong |
d — Full initial dump
Sent in response to a DU request. Contains everything: all modules, logs, file lists, and controller data. The keys of controllerdata and files are detailed in Controller, GlobalDatastore and files; the example below only shows a few of them.
{
"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": {} }
}
}
}m = modules, p = properties, e = elements, l = globallovs, f = profile.
Normalize these to their full names when storing internally.
aa — Full module dump (module loaded/reloaded)
Sent when a module is loaded or reloaded at runtime. Contains the complete module state. Key starts with "aa".
{
"aa-dump all data": {
"Guider": {
"infos": { "modulelabel": "Guider", "description": "..." },
"f": { "name": "default", "changed": false },
"l": {},
"p": {
"status": { ... }
}
}
}
}ap — Full property replacement
Replaces one or more properties entirely. Key starts with "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 — Set all element values (property-level update)
The most common update message. Sends the current values of all elements in a property. Contains values only — no metadata. Key is exactly "ea".
{
"ea": {
"Allsky": {
"p": {
"coming": {
"e": {
"sunrise": { "hh": 7, "mm": 6, "ss": 51 },
"sunset": { "hh": 21, "mm": 15, "ss": 1 }
}
}
}
}
}
}For simple types (int, float, bool, string, light), the value is a scalar:
{
"ea": {
"Focus": {
"p": {
"parameters": {
"e": {
"iterations": 5,
"exposure": 3.5,
"active": true
}
}
}
}
}
}ee — Set one element value
Updates a single element’s value. Same format as ea but for one element only. Key is exactly "ee".
{
"ee": {
"Focus": {
"p": {
"parameters": {
"e": {
"iterations": 7
}
}
}
}
}
}ee is backend-initiated onlyee is only ever sent on the backend’s own initiative (e.g. a device-driven value change, internal polling) — never as a direct response to a client write. Any response to a client-initiated SV or SA is always a full-property ea, regardless of how many elements actually changed.
ev — Set element with metadata
Updates an element’s value and its constraints (min, max, format). Key starts with "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 — Local LOV changed
Sent when an element’s own local list of values (listOfValues) is added to, updated, or cleared on the server’s own initiative — e.g. a filter wheel’s slot names being (re)read from the device. Key starts with "el". Same payload shape as ev (current value, constraints, and listOfValues are all included), but unlike ev this never marks a hasprofile property’s profile as changed (fc): a local LOV is decorative, not a stored profile value.
{
"el-element's own local lov changed": {
"Sequencer": {
"p": {
"sequence": {
"e": {
"filter": {
"value": 1,
"listOfValues": { "1": "Luminance", "2": "Red", "3": "Green", "4": "Blue" }
}
}
}
}
}
}
}ps — Property status update
Updates only the status and enabled fields of a property. Key starts with "ps".
{
"ps-only property state": {
"Focus": {
"p": {
"autofocus": {
"status": 2,
"enabled": true
}
}
}
}
}Status values: 0 Standby · 1 OK · 2 Busy · 3 Error
dm — Module deleted
Sent when a module is unloaded. The client should remove all data for this module. Key starts with "dm".
{
"dm-delete/remove module": {
"Focus": {}
}
}dp — Property deleted
Sent when a property is removed from a module. Key starts with "dp".
{
"dp-delete/remove property": {
"Focus": {
"p": {
"oldProperty": ""
}
}
}
}de — Element deleted
Sent when an element is removed from a property (not the whole property - see dp above). Key starts with "de".
{
"de-delete/remove element": {
"Allsky": {
"p": {
"actions": {
"e": {
"startsequence": ""
}
}
}
}
}
}gc — Grid line created
Sent after a new row is added to a grid property. Key starts with "gc".
{
"gc-grid new line ": {
"Sequencer": {
"p": {
"sequence": {
"i": 3,
"values": {
"target": "M31",
"exposure": 120,
"count": 10
}
}
}
}
}
}i is the index of the newly created row. values is an object keyed by element name.
gu — Grid line updated
Sent when an existing grid row is modified. Key starts with "gu".
{
"gu-grid update line": {
"Sequencer": {
"p": {
"sequence": {
"i": 1,
"values": {
"target": "M42",
"exposure": 60,
"count": 20
}
}
}
}
}
}gd — Grid line deleted
Sent when a grid row is removed. Key starts with "gd".
{
"gd-grid delete line": {
"Sequencer": {
"p": {
"sequence": {
"i": 0
}
}
}
}
}i is the (zero-based) index of the deleted row. Rows after i shift down by one.
gr — Grid reset
Clears the entire grid for a property. Key starts with "gr".
{
"gr-grid reset": {
"Sequencer": {
"p": {
"sequence": {}
}
}
}
}lc / lu / ld — LOV events
lc — LOV created/merged (key starts with "lc"):
Module LOV:
{
"lc-lov create": {
"Focus": {
"l": {
"myLov": {
"label": "My LOV",
"type": "string",
"values": { "a": "Option A", "b": "Option B" }
}
}
}
}
}Controller LOV (no module wrapper, uses lovs key):
{
"lc-lov create": {
"lovs": {
"sharedLov": {
"label": "Shared LOV",
"type": "string",
"values": { "x": "Value X" }
}
}
}
}lu — LOV replaced (same format as lc, full replacement).
ld — LOV deleted (key starts with "ld"):
{
"ld-lov delete": {
"lovs": {
"sharedLov": {}
}
}
}fs / fl / fc — Profile events
Sent when a profile is saved, loaded, or modified.
{
"fs-profile saved": {
"Focus": {
"f": { "name": "myProfile", "changed": false }
}
}
}| Key prefix | Event |
|---|---|
fs | Profile saved |
fl | Profile loaded |
fc | Profile has unsaved changes |
l — Log entry
Sent for every log message generated by the server. Key is exactly "l".
{
"l": {
"d": "2024-01-01T12:00:00.000",
"c": "Focus",
"t": "Autofocus completed successfully",
"l": 1
}
}| Field | Description |
|---|---|
d | ISO 8601 datetime with milliseconds |
c | Context/source (module name or "WS") |
t | Message text (translated to client language) |
l | Log level: 0 Debug · 1 Info · 2 Warning · 3 Error · 4 Critical |
uc — Controller data update
Sent when global controller data changes: profiles, configurations, active INDI drivers, file lists (files, folders), system load (systemwatcher, every 10 s by default). Key starts with "uc". The possible keys are described in Controller, GlobalDatastore and files.
{
"uc-update controller data": {
"profiles": {
"Focus": ["default", "highres"],
"Guider": ["default"]
}
}
}The payload is {"key": value} where value can be a string, array, or object. Each message carries a single key, and its value entirely replaces the previous one.
af / df / ad / dd — File and folder events
Sent to all clients when the content of the watched file folder changes (see File system). Unlike other messages, the value is a bare string: the affected path, not wrapped in an object or array — the server only ever reports one path per message.
{ "af": "/Allsky/image_001.jpeg" }
{ "df": "/Allsky/old_image.jpeg" }| Key | Meaning |
|---|---|
af | File added to the selected folder |
df | File removed from the selected folder |
ad | Sub-folder added anywhere in the webroot |
dd | Sub-folder removed anywhere in the webroot |
af/df only concern the folder currently selected via FS — nothing is reported for other folders’ content. ad/dd are global: they fire for the whole webroot tree regardless of what is currently selected. See File system for the full picture.
qa — Answer to a module query
See the note on QY: this mechanism exists primarily for one module to query another internally. Documented here for completeness, not as a client-facing feature.
Reply to a QY query. QY/qa is a generic, per-module request/answer mechanism: a client (or another module, internally) asks a module a named question with parameters, and gets a named answer back. What query names a module actually understands, and what its answer looks like, is entirely up to that module — the protocol only defines the envelope, not the content.
{
"qa-query answer": {
"Sequencer": {
"q": {
"query": "profileduration",
"result": { "seconds": 3600 }
}
}
}
}result is whatever the module returned for that query — its shape depends entirely on query. A module with no matching case for the requested query name returns an empty result, silently.
Today only the Sequencer module implements a real answer (profileduration), used internally by the Planner module rather than by any documented client workflow. No other shipped module responds meaningfully to any query name — this mechanism exists and is fully wired, but there is little to query in practice right now.
xx — Keepalive pong
Sent by the server in response to a client heartbeat (XX). No useful payload; clients can ignore it.
{ "xx": {} }