# Configuration and the Luna API Everything the UI does goes through the service's Luna API, so anything the UI can do you can also do from an ssh session with `luna-send`. ## Where the settings live ```text /var/lib/webosbrew/audiocap/config.json when the service can write there /tmp/audiocap-config.json fallback, lost on reboot $AUDIOCAP_CONFIG overrides both ``` The service falls back to `/tmp` when it is not running as root, and the UI says so under *System → Settings file*. Settings written there survive the session but not the TV. Edits are merged, not replaced: sending `{"hyperhdr":{"port":5005}}` changes the port and leaves everything else alone. Writes are atomic — a temporary file, `fsync`, `rename` — so a power cut during a save cannot leave a truncated config behind. ## Settings ### Top level | Key | Default | Meaning | | --- | --- | --- | | `autoStart` | `false` | start capturing as soon as the service starts | | `logLevel` | `"info"` | `error`, `warn`, `info` or `debug` | | `sinks` | `["hyperhdr"]` | which outputs to open | `autoStart` only matters if something starts the service at boot — that is what the *Start on boot* toggle installs, a script in `/var/lib/webosbrew/init.d/` that pokes the service so the Homebrew Channel launches it. ### `capture` | Key | Default | Meaning | | --- | --- | --- | | `backend` | `"auto"` | `auto`, `pulse`, `alsa`, `exec` or `tone` | | `device` | `""` | PulseAudio source, or an ALSA PCM like `hw:0,0` | | `server` | `""` | PulseAudio server address; blank autodetects | | `command` | `""` | for the `exec` backend | | `rate` | `48000` | 44100 or 48000 | | `channels` | `2` | 1 or 2 | `auto` tries PulseAudio, then ALSA. It never tries `exec` — that one needs a command only you can supply. The backends: | id | Name | Notes | | --- | --- | --- | | `pulse` | PulseAudio monitor | records the monitor source of the active sink; `libpulse.so.0` is `dlopen`ed at run time, so a TV without it simply reports the backend unavailable | | `alsa` | ALSA PCM | same arrangement with `libasound.so.2` | | `exec` | External command | runs a command and reads raw interleaved S16LE from its stdout, e.g. `arecord -D hw:0,0 -f S16_LE -r 48000 -c 2 -t raw` | | `tone` | Test tone | a sweep; proves the network path without touching the TV's audio at all | ### `dsp` | Key | Default | Meaning | | --- | --- | --- | | `attack` | `0.6` | seconds for the reported level to catch a rise | | `release` | `0.12` | seconds for it to fall away | These shape the numbers in the status document and the on-TV visualiser. They do not touch the audio sent to any sink. ### `hyperhdr` — RTP/L16 audio | Key | Default | Meaning | | --- | --- | --- | | `host` | `""` | the receiving machine | | `port` | `5004` | UDP port | | `multicast` | `false` | send to a group instead of a host | | `multicastTtl` | `4` | hop limit when multicasting | | `sapAnnounce` | `true` | announce over SAP so PulseAudio can find the stream | Payload type 96, 16-bit big-endian PCM, packets kept under 1400 bytes of payload so nothing fragments on a normal Ethernet MTU. ### `hyperhdrViz` — Flatbuffers images | Key | Default | Meaning | | --- | --- | --- | | `host` | `""` | HyperHDR's address | | `port` | `19400` | Flatbuffers port | | `priority` | `150` | HyperHDR priority; lower wins | | `width` / `height` | `64` / `36` | image size | | `fps` | `30` | frames per second | | `mode` | `"spectrum"` | `spectrum`, `level` or `pulse` | | `saturation` | `1.0` | colour intensity | | `minBrightness` | `0.02` | floor so the lights never go fully black | ### `hyperhdrAdjust` — brightness only, via HyperHDR's JSON-RPC Sends no image at all. Instead it calls HyperHDR's `adjustment` command — a post-processing stage that scales output brightness regardless of which priority is currently active — so an existing grabber or effect keeps deciding colour and only overall brightness reacts to sound. | Key | Default | Meaning | | --- | --- | --- | | `host` | `""` | HyperHDR's address | | `port` | `19444` | HyperHDR's classic JSON-RPC port (not 8090, not 19400) | | `level` | `"rms"` | `rms` (steadier) or `peak` (punchier) | | `minScale` | `0.3` | brightness during silence; `1.0` is HyperHDR's normal level | | `maxScale` | `1.3` | brightness at full level; HyperHDR accepts up to `2.0` | ### `udp`, `tcp`, `http` | Key | Default | Meaning | | --- | --- | --- | | `udp.host` | `""` | destination; a host, a multicast group, or `255.255.255.255` | | `udp.port` | `4010` | | | `udp.multicastTtl` | `4` | | | `tcp.port` | `4011` | the TV listens on this | | `tcp.maxClients` | `4` | | | `http.port` | `4012` | `/audio.wav` and `/audio.raw` | | `http.maxClients` | `4` | | All three carry interleaved S16**LE** — little-endian, unlike the RTP sink, because that is what everything reading a raw pipe expects. --- ## The Luna API Service name `org.webosbrew.audiocap.service`, all methods on `/`. ```sh luna-send -n 1 -f luna://org.webosbrew.audiocap.service/getStatus '{}' ``` | Method | Payload | Reply | | --- | --- | --- | | `start` | optional settings patch, applied and saved first | the status document | | `stop` | `{}` | the status document | | `getStatus` | `{"subscribe":true}` for a feed every 100 ms | the status document | | `isRunning` | `{}` | `{isRunning, state}` | | `getConfig` | `{}` | `{path, persistent, settings}` | | `setConfig` | a patch, bare or under `settings` | `{saved, restartRequired, settings}` | | `resetConfig` | `{}` | `{saved, settings}` | | `listBackends` | `{}` | `{backends:[{id,name,description,available}]}` | | `listSinks` | `{}` | `{sinks:[{id,name,description}]}` | | `getDiagnostics` | `{}` | `{backends, system}` — see below | | `getLogs` | `{"clear":true}` optional | `{logs}` | | `quit` | `{}` | ends the process; the next call starts a new one | `setConfig` reports `restartRequired: true` when the capture is running, because most settings are read when a run starts. ### The status document ```json { "returnValue": true, "state": "running", "running": true, "error": null, "capture": { "backend": "pulse", "backendName": "PulseAudio monitor", "device": "…monitor", "rate": 48000, "channels": 2, "frames": 4915200, "blocks": 9600, "timeouts": 0, "uptimeMs": 102400 }, "levels": { "peak": 0.42, "rms": 0.19, "peakDb": -7.5, "rmsDb": -14.4, "clipping": false, "bands": [0.0, "…16 values…"] }, "sinks": [ { "id": "hyperhdr", "ok": true, "name": "HyperHDR audio (RTP)", "error": null, "target": "192.168.1.50", "port": 5004, "packetsSent": 12345, "bytesSent": 4321000, "sendErrors": 0 } ], "configPath": "/var/lib/webosbrew/audiocap/config.json", "configPersistent": true } ``` `state` is `stopped`, `starting`, `running` or `error`. A sink that failed to open reports `ok: false` and an `error`, and the run continues without it — one broken output does not take the others down. Each sink adds its own fields. `packetsSent`/`bytesSent`/`sendErrors` for the datagram sinks, `clients`/`droppedBytes` for the stream servers, `connected`/`registered`/`framesSent`/`connectFailures`/`lastError` for the visualiser, `connected`/`updatesSent`/`minScale`/`maxScale`/`lastError` for the brightness sink. ### Diagnostics ```json { "backends": [{ "id": "pulse", "name": "…", "available": false }], "system": { "root": true, "uid": 0, "libraries": { "libpulse.so.0": "/usr/lib/libpulse.so.0", "libasound.so.2": null }, "binaries": { "parec": false, "pactl": true }, "pulseSockets": ["/var/run/pulse/native"], "pactlSources": "…", "alsaCards": ["0 [Loopback]: …"], "alsaCapturePcms": ["00-01: …"] } } ``` This is the fastest way to find out why a backend reports itself unavailable. `tools/tv-probe.sh` collects the same picture from a shell, plus a few things the service does not look at. ## Doing it from a shell ```sh # change settings and start in one call (the patch is saved, like setConfig) luna-send -n 1 -f luna://org.webosbrew.audiocap.service/start \ '{"capture":{"backend":"tone"},"sinks":["http"]}' # point the RTP sink somewhere else and keep it luna-send -n 1 -f luna://org.webosbrew.audiocap.service/setConfig \ '{"settings":{"hyperhdr":{"host":"192.168.1.50"}}}' # watch the level luna-send -i -f luna://org.webosbrew.audiocap.service/getStatus '{"subscribe":true}' # what went wrong luna-send -n 1 -f luna://org.webosbrew.audiocap.service/getLogs '{}' ```