Skip to content

Manager API

foonerd edited this page Sep 28, 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. 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 / 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.
{
  "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 },
  "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.
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), width, height, bytes, active, 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. { "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.
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.
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.

The catalog

Route What it does
GET /api/catalog { "fetchedAt", "updated", "source", "error", "entries": [ ... ] }. Reads the index when none is kept. Each entry: name, kind (meters, spectrum, both), 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.
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 the limit, 400 empty or bad-name.
GET /api/jobs { "jobs": [ ... ] }, newest first, the last fifty.
GET /api/jobs/:id One job.

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": "meters", "files": 12, "bytes": 1300000, "names": ["..."] } ],
  "error": null, "startedAt": "...", "endedAt": null
}
kind state in order
install, upload queued, downloading (install only), checking, unpacking, rendering, then done or failed
upgrade, rollback queued, downloading or staging, verifying, backing-up, applying, restarting, or failed

error on a failed job is { "error": "<code>", "message": "..." }: checksum, size, bad-theme, units-differ, network, and the like. 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; freed lists the styles that were set in it and went back to the built-in face.

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 (state, infinity, config, showing, 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/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. 400 bad-command for a name that is not a word.
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.

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.

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, named by its manifest or by the file, with a number 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", "latest": { "version", "notes", "bytes", "sha256", "url", "publishedAt" }, "checkedAt", "available", "previous": { "version", "at", "bytes" }, "last": { "from", "to", "phase", "ok", "error", "at" } }. 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/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.

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" } ] }.
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), 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 theme, preview, entry, job, backup, asset or route.
409 An upgrade while one runs.
413 An upload above the limit.
500 A render that failed, or a cache that could not be cleared.
502 The catalog's index, a thumbnail or GitHub could not be read.

Clone this wiki locally