Skip to content

Manager API

foonerd edited this page Oct 7, 2026 · 39 revisions

Manager API

The Manager is a web application the plugin serves on the player, on port 5582 unless the settings page moved it. Its page uses the routes below; so can anything else on the network: a script, a home automation system, a second page. There is no login, as with Volumio's own interface: the API is for the network the player is on.

Every route is under /api and answers JSON, except the page itself and the pictures and files it serves. Bodies are JSON with Content-Type: application/json, except the uploads, which take the file (a zip, a backup zip, a font) as the raw body. A JSON body may be 1 MB at most. An error is { "error": "<code>", "message": "..." } with a 4xx or 5xx status; a code that starts with GLASS. is the key of one of the plugin's strings, which /api/i18n translates.

curl http://volumio.local:5582/api/status
curl -X POST -H 'Content-Type: application/json' -d '{"mode":"random","interval":60}' http://volumio.local:5582/api/meter

The page and its strings

Route Answer
GET /, GET /manage The page.
GET /api/i18n?lang=xx { "lang": "xx", "strings": { ... } }: the plugin's strings in that language, English behind them. Without lang, the player's language.

Status

Route Answer
GET /api/status The Status tab's data, below.
GET /api/diagnose { "symptoms": [...] }: the symptoms the guided diagnosis knows: no-meters, not-moving, restarts, touch, screen, artwork, remotes, slow, other.
POST /api/diagnose { "symptom" } runs the checks for it and answers { "symptom", "found": bool, "findings": [{ "check", "kind": "cause" | "note" | "ok", "key", "with": {...}, "go" }] }, likely causes first: key names the string (MANAGER_<key> in the strings), with the values it fills in, go the Manager's tab where it is set or null. 400 bad-symptom for one it does not know.
GET /api/diagnose/capture Where a report's capture stands: { "state": "idle" }, or { "state": "capturing" | "sending", "symptom", "startedAt", "until", "from", "to" }, the times in milliseconds and as the journal writes them by the player's clock.
POST /api/diagnose/capture { "symptom" } starts a capture: the checks are run and kept, the log level is raised to verbose, the display starts again, and the level goes back by itself after ten minutes. 400 bad-symptom for a symptom it does not know; 409 capturing while one is under way.
POST /api/diagnose/capture/finish { "text" } ends it: the player's system log is sent through Volumio's submitter, the level goes back, and the report's parts come back: { "symptom", "startedAt", "endedAt", "from", "to", "text", "diagnosis", "log": { "link" } }, or "log": { "error": "not-sent" | "no-reply", "said", "kept": bool } when the log could not be sent, kept saying the file stayed on the player. 409 not-capturing.
POST /api/diagnose/capture/cancel Ends a capture with no report; the level goes back.
GET /api/diagnose/log The system log Volumio's submitter kept on the player when it could not send it, as a download; 404 no-log when there is none.
{
  "version": "0.7.40",
  "arch": "arm",
  "binary": true,
  "running": true,
  "display": "0",
  "headless": false,
  "timeout": 5,
  "activeTheme": "1280x720_g5_710_Turntables",
  "meter": { "theme": "...", "mode": "random", "names": [], "available": ["..."], "onTitle": false, "interval": 60 },
  "channel": { "clients": 1, "status": "play", "service": "mpd", "title": "...", "artist": "..." },
  "showing": { "theme": "1280x720_g5_710_Turntables", "meter": "142G5_01_Pioneer Gold", "rate": 30 },
  "logging": { "level": "warn", "targets": [], "levels": ["error", "warn", "info", "verbose", "trace"], "targetsAvailable": ["audio", "channel", "display", "remotes", "themes", "artwork", "manager", "settings"] },
  "performance": { "profile": "auto", "auto": "standard", "values": { "frameRate": 30, "rotationQuality": "medium", "rotationFps": 8, "transitions": true }, "valuesProfile": "standard", "board": { "model": "Raspberry Pi 5 Model B Rev 1.1", "cores": 4, "arch": "aarch64", "class": "pi5" }, "governor": true },
  "fonts": { "styles": { "light": { "kind": "builtin", "value": "builtin" }, "regular": { "kind": "builtin", "value": "builtin" }, "bold": { "kind": "custom", "value": "My Sans.ttf" }, "italic": { "kind": "builtin", "value": "builtin" }, "digi": { "kind": "builtin", "value": "builtin" } }, "uploaded": 1 },
  "themes": { "meterBase": "/data/INTERNAL/glass/templates", "meters": 40, "spectrumBase": "/data/INTERNAL/glass/templates_spectrum", "spectrum": 18 },
  "sharing": { "shared": true, "roots": ["/data/INTERNAL/glass/templates", "/data/INTERNAL/glass/templates_spectrum"] },
  "artwork": { "enabled": true, "keyMode": "project", "interval": 10, "order": "random", "cachedArtists": 12 },
  "cardash": { "enabled": true, "mode": "clock", "dayTheme": "...", "nightTheme": "...", "period": "day", "next": { "at": "<ISO time>", "period": "night" } },
  "interactive": "theme",
  "face": { "pages": 1, "frames": true, "hops": 1234 },
  "sheet": { "player": { "volumio": "4.204", "hardware": "pi", "board": "Raspberry Pi 5 Model B Rev 1.1", "backendUptimeS": 5400, "build": { "commit": "a19ee43", "built": "<ISO time>" } }, "addresses": [{ "name": "eth0", "address": "192.168.1.20" }], "screen": { "size": "1280x720", "mouse": false, "since": "<ISO time>" }, "audio": { "tapInChain": true, "output": "...", "service": "mpd", "trackType": "flac", "samplerate": "44.1 kHz", "bitdepth": "16 bit" }, "housekeeping": { "newestBackup": { "name": "before-0.7.43", "at": "<ISO time>" }, "problems": [] } },
  "upgrade": { "last": { "from": "0.7.42", "to": "0.7.43", "at": "<ISO time>", "phase": "done" }, "previous": { "version": "0.7.42", "at": "<ISO time>", "bytes": 69804357 } },
  "themeSize": "1280x720",
  "measured": true,
  "remotes": { "enabled": true, "serving": true, "ports": { "frames": 5580, "channel": 5581, "beacon": 5579, "manager": 5582 }, "receiving": 1, "connected": 1, "problem": null },
  "manager": { "port": 5582, "url": "http://volumio.local:5582/", "uptimeS": 780 },
  "catalog": { "fetchedAt": "...", "updated": "...", "count": 179, "installed": 1 },
  "rings": [ { "name": "glasstap.glass.7297.0", "tag": "glass", "pid": 7297, "rate": 44100, "channels": 2, "seq": 12345, "writtenAgoMs": 19, "live": true } ],
  "diskFree": 932000000000,
  "hostname": "volumio",
  "legacy": false,
  "language": "en",
  "jobs": 0
}
Field Meaning
binary Whether the display binary for this player's processor is in place.
running Whether the display is up (its run flag stands).
showing The meter the player's own display shows at the moment, as it last reported it, or null.
display, headless The X display chosen, and whether the player has no screen of its own.
measured While the player reports play: whether a live ring exists, so what plays passes the tap.
rings Every ring under /dev/shm, with its stream and how long ago it was written; live within three seconds.
remotes Whether remotes are served, the ports, how many receive frames, how many are connected, any problem the daemon or the channel reported.
fonts Each text style's face as /api/fonts reports it, without the lists, and how many fonts were uploaded.
themes The two theme folders and how many themes (folders with a meters.txt) and spectrum twins (with a spectrum.txt) each holds.
sharing Whether the theme folders are editable over the network share, and the folders.
artwork The artist fanart switch and settings, and how many artists have pictures cached.
cardash Car Dash as /api/cardash reports it: the settings, the period now and the next switch.
face The Face tab's feed: pages connected, frames whether the daemon's socket is connected, hops passed on since the start.
sheet The status sheet's own rows: player (volumio, hardware, board, backendUptimeS, build as { commit, built } from the zip or null, and from 0.8.2 memory as { totalMb, availableMb, usedMb, swapUsedMb } or null and plugins, the other plugins installed, each category, name, title, version, enabled, running, audioPath, those in the audio path first), addresses (each interface's name and IPv4 address, loopback left out), screen (size from the X server or empty, mouse, since the display started), audio (tapInChain, output, service, trackType, samplerate, bitdepth), housekeeping (newestBackup as { name, at } or null, problems the last three warning or error lines).
upgrade last as the System tab reports it (from, to, at, phase) or null, previous, and test, whether this player is offered test releases.
themeSize The theme on show's size from its folder name, WxH, or empty.
interactive The interactive controls setting: theme, on or off.
legacy Whether PeppyMeter Screensaver is enabled.
jobs Jobs still running.

Themes

Route What it does
GET /api/themes { "active": "...", "meterBase": "...", "spectrumBase": "...", "themes": [ ... ] }. Each theme: folder, meters (the section names), spectrum (whether a spectrum twin exists), spectra (the twin's section names), empty (a folder with no meters.txt), width, height, bytes, files, mtime, active, builtin, previews (none, stale, fresh, queued, rendering), catalog (the catalog entry it came from, or null).
POST /api/themes/:folder/activate Puts the theme on show, and sets the meter selection back to random. { "ok": true, "active": "...", "changed": true }. 400 with invalid, not_found or no_config.
DELETE /api/themes/:folder Removes the theme from both trees and its previews. { "ok": true, "switchedTo": "..." } names the theme put on show when the removed one was. The last theme is refused (GLASS.THEME_REMOVE_LAST), as is a folder that is not a theme (GLASS.THEME_REMOVE_INVALID).
GET /api/themes/:folder/previews { "folder", "state", "meters": [ { "meter", "url", "thumb" } ] }: the rendered pictures and their addresses.
POST /api/themes/:folder/render Draws the previews unless fresh. Body { "force": true } draws again, { "wait": true } answers when done, with the same answer as previews. 404 when the theme has no meters.txt; 500 render-failed with the reason.
GET /api/themes/:folder/preview/:meter The rendered picture, PNG; ?thumb=1 the thumbnail. 404 until rendered.
POST /api/themes/:folder/tailor Body { "width", "height", "stretch": false }: a copy of the theme cut to that size on the player, its shape kept and the copy centred, or stretched to the screen's shape with stretch, installed beside the original with its spectrum twin (a copy of that name is replaced) and its previews drawn. 202 with the job, kind tailor. 400 bad-size (64 to 7680 by 64 to 4320), 404 without a meters.txt. The Tailor page has what the cutter does.
POST /api/themes/:folder/package Body { "settle": 3 } (1 to 30 seconds a meter): the theme as the Catalog takes it, a zip with the theme, its twin and a preview.png that tiles its meters, the first nine where it has more, each shown for settle seconds first. 202 with the job, kind package; the zip comes from GET /api/jobs/:id/file when the job is done. 404 without a meters.txt.
GET /api/themes/:folder/files The theme's files with checksums: { "folder", "files": [ { "path", "sha256", "bytes" } ], "spectrum": { "folder", "files": [ ... ] } } or "spectrum": null. What a remote display syncs from.
GET /api/themes/:folder/file?tree=templates&path=... One file of the theme, from templates or templates_spectrum, only from inside the folder.

A folder name is one the display accepts: letters, digits, spaces, dots, underscores, brackets, plus and hyphens, starting with a letter or digit. Anything else is refused with bad-folder.

Car Dash

Route What it does
GET /api/cardash { "enabled", "mode": "clock" | "sun", "dayTheme", "nightTheme", "dayAt": "07:00", "nightAt": "20:00", "offsetMin", "lat", "lon", "location": { "lat", "lon", "source": "zone" | "manual" | "none", "zone" }, "sun": { "rise", "set" } | null, "bySun", "period": "day" | "night" | null, "next": { "at": "<ISO time>", "period" } | null, "active" }: the settings, the place the sun is reckoned from, today's sunrise and sunset when the sun decides, which period it is now, when the next switch comes and what it brings, and the theme on show.
POST /api/cardash The settings to set: enabled, mode, the two themes, the two times (HH:MM), offsetMin (-180 to 180), lat and lon (both or neither, empty for the zone's place). Applied at once: the period's theme goes on show when it is not, and the timer is set for the next switch. 400 bad-time, same-time, bad-theme (a theme must be installed; both when on), bad-location, bad-offset, or no-location (by the sun with no place known for the zone and none given). Answers with ok and the state as set.

The meter selection

Route What it does
GET /api/meter { "theme", "mode": "random" | "list" | "single", "names": [...], "available": [...], "onTitle": false, "interval": 60 }.
POST /api/meter The same fields to set: mode, names (for list and single), onTitle, interval (15 to 1000 seconds). Answers with ok and the selection as set. The display moves to the new selection at once; a remote that follows the player follows it. 400 GLASS.MANAGER_BAD_REQUEST for a mode that is not one, GLASS.MANAGER_METER_NAMES where none is named or single does not name exactly one, GLASS.MANAGER_METER_UNKNOWN with the unknown names in message.

The catalog

Route What it does
GET /api/catalog { "fetchedAt", "updated", "source", "error", "entries": [ ... ] }. Reads the index when none is kept or the one kept is older than ten minutes; a read that fails keeps the one kept. Each entry: name, kind (meter, meter+spectrum, spectrum), category, width, height, bytes, units (the folders the zip holds, with install, folder, from, kind, names), thumb (a route below, or null), preview (the collection's picture), state (absent, installed, update, present).
POST /api/catalog/refresh Reads the index again; the same answer. 502 with the index's error when it could not be read.
GET /api/catalog/thumb/:name The entry's thumbnail, cached for a day. 400 bad-name, 404 not-found, 502 when the picture could not be fetched.
POST /api/catalog/install { "name": "..." } starts an install; 202 with { "ok": true, "job": { ... } }. 404 not-found for a name not in the index.

Uploads and jobs

Route What it does
POST /api/upload?name=file.zip The theme zip as the request body (or the name in an X-File-Name header). 202 with the job. 413 too-large above 512 MB, 400 empty or bad-name.
GET /api/jobs { "jobs": [ ... ] }, newest first, the last fifty.
GET /api/jobs/:id { "job": { ... } }.
GET /api/jobs/:id/file A finished package job's zip, as an attachment named after the theme. 404 for any other job, or before it is done; the Download link ends when the plugin starts again.

A job:

{
  "id": 3, "kind": "install", "name": "1280x720_Reloop_RL7000",
  "state": "unpacking", "progress": { "done": 812000, "total": 1300000 },
  "folders": [ { "install": "templates", "folder": "1280x720_Reloop_RL7000", "kind": "meter", "files": 12, "bytes": 1300000, "names": ["..."] } ],
  "error": null, "startedAt": "...", "endedAt": null
}
kind state in order
install, upload queued, downloading (install only), checking, unpacking, then done or failed. The previews are drawn after done; GET /api/themes says how they stand (previews).
upgrade, rollback queued, downloading (upgrade only), verifying, backing-up, applying, restarting, or failed
tailor queued, cutting, unpacking, then done or failed; folders names the copy and its twin. The previews are drawn after done.
package queued, packaging, then done or failed; progress counts the meters shown of the theme's count, settle is the seconds a meter, and file the zip's path on the player when done.

error on a failed job is { "error": "<code>", "message": "..." }: checksum, size, bad-theme, units-differ, network, and the like. A download reports network only after it has been made again three times (from Glass 0.8.13); each new attempt is a line in the player's log. Installs, uploads and upgrades run one at a time; a job waits in queued for its turn.

Artwork

Route What it does
GET /api/artwork { "enabled", "keyMode": "personal" | "project", "personalKey", "interval", "order": "sequential" | "random", "transition": "none" | "fade" | "merge", "transitionMs", "unlimited", "maxImages" }.
POST /api/artwork The same fields to set; answers with ok and the settings as set.
POST /api/artwork/clear-cache Drops the cached fanart copies. { "ok": true }.

Fonts

Route What it does
GET /api/fonts styles: for each of light, regular, bold, italic and digi, { "kind": "builtin" | "custom" | "player" | "other", "value" } (the uploaded or player font's file name, or the configured value as it stands); builtIn: which styles have their built-in face on this player; player: { "path", "fonts": [names] }, the fonts under the meter configuration's font.path; custom: [{ "name", "bytes" }], the uploaded fonts.
POST /api/fonts { "styles": { "<style>": "builtin" | "<player font name>" | { "custom": "<uploaded font name>" } } }; a style not given keeps its value. Writes font.<style> in the meter configuration and restarts the display. Answers with ok and the settings as set; bad-font names a font the player does not have.
POST /api/fonts/upload?name=<file name> The font file as the request body, at most 64 MB. Kept on the player under a plain name with the extension its bytes say (TrueType or OpenType, one face per file; not-a-font otherwise); a name already there is replaced. Answers with ok, name and the settings.
DELETE /api/fonts/custom/:name Removes an uploaded font; removed names it and freed lists the styles that were set in it and went back to the built-in face. 404 not-found.

The network share

Route What it does
GET /api/sharing { "shared": false, "roots": ["/data/INTERNAL/glass/templates", "/data/INTERNAL/glass/templates_spectrum"] }: whether the theme folders are writable from computers on Volumio's Internal Storage share, and the folders.
POST /api/sharing { "shared": true } makes every theme folder writable over the share, now and for themes installed later; false makes them readable only. Answers with ok, changed and the state.

The face

Route What it does
GET /api/face/events An event stream (text/event-stream) for the Face tab: hop events carry the frames daemon's datagrams in base64, plugin events the lines the displays hear (config, state, showing, infinity, queue, persist, views, each a JSON object with its kind), the last of each first on connection, and feed events { "frames": true | false } say whether the daemon's socket is connected. The daemon's stream is one upstream connection the manager shares between every page and opens only while a page is connected. 503 no-face before the plugin has started the feed.
GET /face/glass-face.wasm The browser module: the display's pipeline compiled to WebAssembly. 404 no-module when the release has none.
GET /face/glass-evo-face.wasm glass-evo's module (from 0.8.10): the pipeline with glass-evo's face over the theme, from the installed component. 404 no-module where the component is not here or carries none.
GET /api/face/module Which module a page brings (from 0.8.10): { "module": "glass" | "glass-evo", "url", "mode", "has", "theme": { "name", "text" } | null }. mode is the user's choice of what the views show (the next row has the three), has whether a glass-evo with a browser module is installed, theme the face theme the settings name, for the page to put into the module's table at faces/<name>/face.txt.
POST /api/face/views { "mode": "follow" | "face" | "theme" } (from 0.8.10): what the Face tab and Anymote show. follow: glass-evo's face over the theme only while glass-evo owns the player's screen, the theme alone while the kiosk does; face: the face whoever owns the screen; theme: the theme alone, always. Without an installed module (has false) the pages carry the theme alone whatever the mode. A mode never set reads as follow. Answers { "ok", "changed", "mode", "has", "face" }, face saying whether the pages carry the face now; 400 bad-request for another word. Open pages are told through the stream (a plugin line { "kind": "views", "face" }) and bring the other module. GET /api/screen says views: { "mode", "has", "face" }.
GET /face/face-page.js The Face tab's script, the page's side of the module's contract.
POST /api/face/command { "name", "value" }: a command as a display sends it down the channel (toggle, play, pause, stop, next, previous, seek, volume, random, repeat), run the same way; the Face tab sends what a tap or a drag on a control asked for. Answers { "ok": true, "name" }; 400 bad-command for a name that is not a word of lower-case letters.
POST /api/face/meter { "meter" }: from 0.8.71, a meter stepped to in the Face tab; the player's own display is asked to show it. Answers { "ok", "meter", "told" }, told false when no display of the player's own is connected.
GET /api/face/picture?at=<location> A picture for the face, fetched from the player itself: at is a path under the player's own /albumart route with its query (the album art the player reports, or /albumart?sectionimage=<reference> for a fanart picture), proxied to the player as given, or an absolute address, accepted only when it is the very one the player reports as the playing track's album art. Anything else, a non-image answer, an answer that is not 200 or one declared over 32 MB is 404 not-found; a player that does not answer within eight seconds is 502 unreachable.
GET /api/face/fanart?artist=<name>&uri=<track> The artist's fanart set as the display asks the plugin for it: success, images (references for /albumart?sectionimage=), interval_ms, transition, transition_ms, order, source; success: false with an error when there is none, or no artist.
GET /anymote Anymote: the face on a page of its own, filling the window, with a full-screen button.
GET /anymote/manifest.json, GET /anymote/icon.svg The web manifest (display: fullscreen, landscape) and icon, so a phone keeps Anymote on its home screen as a full-screen app.

Interactive controls

Route What it does
GET /api/touch { "interactive": "theme" | "on" | "off" }: whether a theme's indicators and buttons act when tapped, as the theme says, for every theme, or never.
POST /api/touch { "interactive": "theme" | "on" | "off" } sets it; the display starts again with the change and remotes hear it. Answers with ok, changed and the value.

The screen

Route What it does
GET /api/screen { "driver", "rotation": 0 | 90 | 180 | 270, "pointer": "auto" | "show" | "hide", "pointerShown": bool, "faceSize", "kioskActive": bool, "free": bool, "ownX": bool, "fact": {...}, "now": {...}, "owner": {...}, "graphics": {...}, "views": {...}, "probe": {...}, "touch": {...} }: the settings (driver is the key as kept, drawn by fact since 0.7.83), the pointer as resolved, whether the screen is Glass's (free), the fact behind it (xserver, ownX: an X server brought up for the face alone, kiosk as systemd states it, kioskEnabled, touchDisplay, displayConfiguration, panel), what the display draws on now (now.display.running, now.display.driver and now.display.renderer as the display reported, now.wouldDraw), and the probe: board, connectors (name, status, native size, portrait), inputs (touch, mice, keyboards, remotes), backlights, holders (kiosk, xserver, touchDisplay, displayConfiguration), touchDisplayAngle, suggestion (rotation, pointer, panel). Also faceSize, and owner: who owns the screen (owner: kiosk or glass-evo), the glass-evo component (evo: installed, available, version, binary, arch, requires, and face, its browser module's path or null), the register of the last take, and here, whether glass-evo is on the player at all; from 0.8.0 mode, how glass-evo would hold this screen (kms: on the screen itself; x: on an X server of its own; null: not at all) with holdable, and in the register gaveBackBecause (face-failed, component-missing, plugin-stopped) where the screen went back by itself. Beside free, ownX says the screen is Glass's through an X server brought up for it. views is what the Face tab and Anymote show: { "mode", "has", "face" } (the Face and Anymote section has the modes). touch is the touch mapping as POST /api/screen/touch answers it: mapping, swap, flipX, flipY, matrix, calibration.
GET /api/screen/probe The probe alone, read afresh.
POST /api/screen/touch { "mapping": "auto" | "overrides" | "calibrated", "swap", "flipX", "flipY" } sets the touch mapping; calibrated is refused with GLASS.MANAGER_TOUCH_NOT_CALIBRATED until a calibration is kept. The display starts again with the change. Answers with the touch settings: mapping, the switches, matrix and calibration (state none, waiting, done or failed, with worst in pixels or error).
POST /api/screen/calibrate Asks the display for a calibration: five targets on the screen. Answers { "ok", "calibration" }; the state is read back through GET /api/screen under touch.calibration. A calibration not finished within two minutes ends failed with timeout.
POST /api/screen { "rotation", "pointer", "faceSize" } sets them (faceSize is normal, large or car, the size glass-evo draws at), the pointer resolved from the probe; a driver in the body is ignored, the screen being drawn by fact. The display starts again with the change. Answers with ok and the values; 400 GLASS.MANAGER_BAD_REQUEST for a rotation, a pointer or a face size that is not one.
POST /api/screen/owner { "owner": "glass-evo" | "kiosk" } hands the screen to glass-evo or back to the kiosk, where the glass-evo component is installed (GLASS.MANAGER_OWNER_EVO_ABSENT where it is not; 400 GLASS.MANAGER_BAD_REQUEST for another word). A take turns the kiosk's plugins off through the player's plugin manager, stops and disables the kiosk's units and records each change; the way back restores what the take changed where it is still as the take left it. Answers with ok, changed and screen, the screen route's answer; GLASS.MANAGER_OWNER_FAILED when a step did not finish, with what failed as message (under data.message before 0.8.40); from 0.8.37, where glass-evo would draw on the screen itself and the graphics check fails, GLASS.MANAGER_OWNER_NO_GRAPHICS with the reason as message and nothing turned off, unless the request carries "force": true; from 0.8.0 GLASS.MANAGER_OWNER_NO_SCREEN where the player has neither a screen the kernel drives nor an X server, with nothing turned off.
GET /api/face The look of glass-evo's face: settings (the face.<name> keys of the display's configuration, by name), builtIn (the built-in look's keys as the component writes them out), looks (each face theme with name, shipped and its keys, the user's own before those the component ships), themeLook (from 0.8.50: what the theme on show brings for the face, { folder, keys } from the face.txt in its folder, which the face lays over the chosen look and under the settings; null where it brings none) and frostSuits (whether frost over a moving theme suits the board).
GET /api/backgrounds The user's pictures for the screen when nothing plays (from 0.8.63): { "backgrounds": [{ "name", "bytes" }] }, the files of /data/INTERNAL/glass/backgrounds that are named as a picture. GET /api/backgrounds/<name>/file serves one. POST /api/backgrounds/upload?name=<file name> takes a picture as the request's body (JPEG, PNG or WebP by its first bytes, at most 32 MB) and answers with the name it is kept under and the list; 400 not-a-picture or bad-name, 413 over the limit. DELETE /api/backgrounds/<name> removes one, and with it the choice where it was the one on show. The choice itself is the face's setting idle.picture, the darkening idle.dim; GET /api/face lists the names as backgrounds.
POST /api/face { "set": { "<name>": value | null }, "reset": bool } sets the face's settings: a value sets a key, null removes it, reset removes every key of the look first. A name is up to four words of letters and digits with dots between, a value a short word, a number, a colour or a pattern (bad-name, bad-value); the size and the board's word on frost are not set here. The display starts again with the change. Answers with ok, changed and what GET /api/face gives.
GET /look/lookmodel.js The model behind the Screen tab's look panel, the script the page shares with its tests.

Settings the manager owns

Route What it does
GET /api/settings { "themeTagRules": "...", "doNotDeleteThemes": false, "managerPort": 5582, "managerHost": "" }.
POST /api/settings themeTagRules and doNotDeleteThemes to set. The port and address are set on the settings page.

Where the theme goes on the screen

Route What it does
GET /api/display { "fit": false, "position": "center" | "manual", "x": 0, "y": 0 }: whether the theme is scaled to the screen with its shape kept, and where it goes, centred or with its top left at x, y. The Appearance tab's Display section and the Settings page's Meter Position and Fit to screen are the same keys.
POST /api/display The same fields to set; the meter configuration is written, the displays hear of the change, and the player's display starts again with it. 400 GLASS.MANAGER_BAD_REQUEST for a position that is not center or manual, or coordinates out of -7680 to 7680 by -4320 to 4320 (negative from 0.8.11). Answers with ok and the placement as set.

The old plugin's themes

Route What it does
GET /api/legacy { "present", "themes", "bytes", "installed" }: whether PeppyMeter Screensaver's folder still holds a templates tree, how many theme folders and how many bytes in both trees, and whether that plugin is still installed. The System tab shows its panel while present.
POST /api/legacy/wipe Removes that folder's templates and templates_spectrum trees, nothing else. Answers with ok, removed (the trees removed) and the state after.

Backups

Route What it does
GET /api/backups { "backups": [ { "name", "created", "pluginVersion" } ] }, newest first.
POST /api/backups { "name": "..." } creates one; answers with the list. 400 with the plugin's backup codes when the name is bad or taken, or storage is short.
POST /api/backups/:name/restore Restores it; { "ok": true }. 400 with the plugin's codes when the backup is damaged or from a newer version.
DELETE /api/backups/:name Removes it; answers with the list.
GET /api/backups/:name/download The backup as glass-backup-<name>.zip: manifest.json, config.json, peppymeter_config.txt, spectrum_config.txt.
POST /api/backups/upload?name=file.zip The zip as the request body becomes a backup (32 MB at most, each file in it 4 MB), named by the file in name without glass-backup- and its extension, or by its manifest when no name is given, with -2, -3 behind it when the name is taken; the four files may sit at the zip root or under one folder. { "ok": true, "name": "...", "backups": [ ... ] }. 400 bad-backup when the files are not all there, or with the plugin's codes when they do not parse; 413 when too large.

Upgrades

Route What it does
GET /api/update { "current", "test", "latest": { "version", "tag", "name", "notes", "page", "prerelease", "bytes", "sha256", "url", "publishedAt" }, "checkedAt", "available", "previous": { "version", "at", "bytes" }, "last": { "from", "to", "phase", "ok", "error", "backup", "at", "endedAt" } }. test is whether this player is offered test releases, prerelease whether the release named is one. Reads GitHub when the last look is older than a day; with an error field when it could not, and the last known values.
POST /api/update/check Reads the latest release now. 502 when GitHub could not be read.
POST /api/update/test { "test": true } or false (from 0.8.5): whether this player is offered test releases, the pre-releases of Glass and of glass-evo. Both releases are looked up again at once; answers { "ok", "changed", "test", "update", "evo" }, the last two as GET /api/update and GET /api/evo give them. 400 when test is not a boolean. GET /api/update and GET /api/evo say test, and a latest that is a pre-release says "prerelease": true.
POST /api/update/install Upgrades to the latest release; 202 with the job. 400 up-to-date, 409 busy while an upgrade runs. The backend restarts a few seconds after the job reaches restarting; the job is gone with it, and GET /api/status answers with the new version once it is up.
POST /api/update/rollback Puts the kept previous version back the same way. 400 no-previous.
GET /api/stable What the way back to the stable release would do here (from 0.8.52): glass and evo, each { "action": "none" | "back" | "forward", "from", "to" } (evo.from null where glass-evo is not installed); facts (holds: glass-evo holds the screen, evo: it is installed, tests: the player takes test releases); questions, the parts asked on this player in their order, each { "id", "keep" } with the suggested answer (screen, network, owner, show, look, behaviour, tuning, tests). The releases are read from GitHub at every call; error where they could not be.
POST /api/stable { "keep": { "<part>": true | false } } starts the act as a job, 202 with the job and the answers as taken; an answer left out is the suggested one, and a part not asked on this player is not kept. The job goes through verifying, downloading, backing-up, applying and ends in restarting, as an upgrade's; job.same is true where Glass stays at its version and only the backend restarts. 409 busy while an upgrade runs, 502 where the releases could not be read.
GET /api/evo glass-evo as a component (from 0.8.0): installed (version, available: this player's binary is there, requires: the least Glass it names) or null, glass (this Glass), least (the least glass-evo this Glass works with), outdated (installed and older than that), latest (the release as GET /api/update gives one), available (a release to get or update to), behind (the latest release is itself older than this Glass works with), previous (the version kept for going back, { version, requires }), owner (who owns the screen), test (whether test releases are offered), checkedAt; with an error field and the last known values when GitHub could not be read.
POST /api/evo/check Reads the latest release now. 502 when GitHub could not be read.
POST /api/evo/install Gets the latest release, or updates to it; 202 with the job (kind: "component", states downloading, verifying, applying, done or failed). 400 up-to-date or too-old, 409 busy. A job fails with checksum, bad-manifest, needs-glass, too-old, no-binary, no-module (a component whose manifest names a browser module it does not hold, from 0.8.10) or a download's own size, network or no-digest, and nothing is changed. A face on the screen starts again as the new one.
POST /api/evo/rollback Puts the kept version back, and keeps the one in place instead. 400 no-previous; a kept version this Glass does not work with fails the job.
POST /api/evo/remove Removes the component and the kept version. 409 owns-screen while glass-evo owns the screen.

Logging

Route What it does
GET /api/logging { "level": "warn", "targets": [], "levels": [...], "targetsAvailable": [...] }.
POST /api/logging level and targets to set; applied at once for the plugin, at the next start of the display and the daemon. Answers with the settings as set. The System tab's Logging panel uses these.
GET /api/logs?lines=300 { "lines": [...], "total": n }: the last Glass lines from the player's journal, newest last, 20 to 3000. With download=1, the lines as a text file named after the player and the time. 500 journal when the journal could not be read.

Performance

Route What it does
GET /api/performance { "profile": "auto", "auto": "standard", "values": { "frameRate", "rotationQuality", "rotationFps", "transitions" }, "valuesProfile": "standard", "board": { "model", "cores", "arch", "class" }, "profiles": { ... }, "governor": true, "showing": { "theme", "meter", "rate" } }. showing.rate is the rate the display runs at, which the governor may have lowered.
POST /api/performance { "profile": "auto" | "full" | "standard" | "light" | "minimal" | "custom", "governor": true }, either or both: applies the profile's values to the meter configuration and the settings page, and the display starts again with them; custom only records the choice; governor turns the frame rate governor on or off. Answers with changed and the same as the GET. 400 bad-profile.

Remote displays

Route What it does
GET /api/remote/status { "ports": { "enabled", "frames", "channel", "beacon", "manager" }, "beacon": { ... as sent ... }, "serve": { "at", "port", "rate", "ring", "sent", "subscribers": [ { "address", "id", "name", "release", "sent", "since" } ] } or null, "serving", "errors": { "serve", "channel" }, "headless", "remotes": [ { "id", "name", "release", "screen", "page", "address", "since" } ] }. From 0.8.58 each of remotes carries face (the face a bundle is built with, empty on the standalone) and upgrade: { "product", "version", "latest", "behind" }, behind true, false, or null where it cannot be said (no version named, no release seen, or the release last seen is a test release).
GET /api/remote/settings { "enabled", "framesPort", "channelPort", "beaconPort", "managerPort" }.
POST /api/remote/settings enabled, framesPort, channelPort, beaconPort to set; applied at once. 400 GLASS.MANAGER_RM_PORTS_INVALID or GLASS.MANAGER_RM_PORTS_CLASH. Answers with ok, changed and the settings as set.
GET /api/remote/config What a remote syncs: version, release, theme, meter, files.meter and files.spectrum (the configuration texts), face (from 0.8.20, for a remote built with a face: owner, kiosk or glass-evo, who owns the player's screen, and theme, the face theme the settings name as { name, text } or null), assets.fonts, assets.icons, assets.webfonts and assets.custom (name, sha256, bytes): the plugin's fonts, the format icons (the plugin's own, then Volumio's beside its web application), the player's web fonts (the font files in the directory the meter configuration's font.path names) and the uploaded fonts.
GET /api/remote/asset/font/:name, GET /api/remote/asset/icon/:name, GET /api/remote/asset/webfont/:name, GET /api/remote/asset/custom/:name One of the plugin's fonts, one of the format icons (the plugin's own before Volumio's, as the display looks), one of the player's web fonts, or one of the uploaded fonts.
GET /api/remote/track-file?uri=...&name=... One picture from the playing track's folder: uri as the player reports the track, name a plain file name with a picture extension. Only files inside that folder under /mnt, up to 32 MB. 404 not-found otherwise.

The Remotes page has the wire between the player and a remote, and the remote's own page API.

Errors

Status When
400 A bad name, folder, meter or body; a refused change, with the reason in error.
404 No such preview, catalog entry, job, font, asset, file or route, and a theme with no meters.txt for the routes that need one. A backup that is not there, or a theme to put on show or remove that is not there, is 400 with the plugin's code.
409 An upgrade, a rollback or a glass-evo job while one runs (busy); a capture while one is under way (capturing), none to finish (not-capturing) or its log on its way (sending); removing glass-evo while it owns the screen (owns-screen).
413 An upload above the limit.
500 A render that failed, a cache that could not be cleared, or the journal that could not be read (journal).
502 The catalog's index, a thumbnail or GitHub could not be read; a picture the player did not answer for (unreachable).
503 no-face: the Face tab's stream before the plugin has started its feed.

Clone this wiki locally