Commands, settings, SDK functions, anything in the docs.

Use the CLI

Scripting

JSON output, exit codes, commands that take input on stdin, and several accounts.

On this page

Every command is meant to be as easy to use from a script, or an agent, as at a terminal: results are JSON, failures are JSON, and the exit code says what kind of failure it was.

Output

A command prints its result as JSON on stdout, indented for reading. With --json (or -j) it prints the same JSON on one line. Either form goes straight into a tool such as jq:

Terminal
ledable devices list --json | jq -r '.[].nickname'

Commands that print as they go, such as ble scan, devices logs and the page commands below, print one JSON object per line, with or without --json.

Everything meant for a person goes to stderr: prompts, progress such as Sent a sign-in link…, and failures. stdout carries results only.

Failures and exit codes

A command that fails writes one JSON object to stderr, on one line, and exits with a code other than 0:

JSON
{"error":{"code":"usage","message":"Specify the device with --device <id>"}}
Exit codecodeWhat happened
0The command did what it was asked
1operation_failed, or a code from LEDABLE's serversIt could not be done
2usageAn argument, option, file, stdin line or setting value was wrong
124timeoutThe command ran out of time
130interruptedCtrl-C, or the process received SIGINT or SIGTERM

When LEDABLE's servers refuse a request, code says why: unauthorized when the sign-in is no longer accepted (sign in again), transport when the network failed, or a specific reason such as deviceNotFound. message says what went wrong, in the phone app's words where it has some.

Timeouts

Every command gives up after a time of its own, which --help and the command reference show: 30 seconds for most, longer for those that wait on a person or a display. --timeout <seconds> sets another, up to 86400 (a day):

Terminal
ledable devices logs --device "$DEVICE" --timeout 3600

A command that runs out of time fails with exit code 124. The exceptions are the commands that watch something until you stop them, such as devices logs or devices presence: for them the timeout is how long to watch, and reaching it ends them with exit code 0.

Secrets in scripts

A password, token or one-time code is never an argument, where shell history and the process list would keep it. At a terminal the CLI asks for it without showing what you type. In a script, pipe it into stdin, one line per secret:

  • wifi join --password-stdin reads the Wi‑Fi password.
  • auth login --code-stdin reads the one-time code from the sign-in page.
  • store install and playlist configure read one line for each --credential field, in the order you named them, whenever stdin is not a terminal.
Terminal
printf '%s\n' "$PASS" | ledable wifi join Home -d "$DEVICE" --password-stdin

Page commands

Some commands run one of the app's pages instead of doing one thing: the same page, with the same rules, the phone shows. Their descriptions in --help start with "Run the app's …":

CommandPage
ble linkPairing
wifi setupWi‑Fi
playlist pagePlaylist
playback controlPlayer
settings pageDisplay settings
firmware pageFirmware
devices logs-pageLogs
devices presenceOnline status

A page command prints a line {"state": …} each time what the page shows changes. It reads stdin one line at a time: the first word names what to do, as tapping a control would, and the rest of the line is its argument, kept whole so that a network name may contain spaces. The lines a page takes are listed under "Stdin, one line at a time" in its --help and in the reference. A line the page does not know is reported on stderr, and the page carries on. A line sent before the page can act on it may be refused or ignored, so wait for the state that offers it.

A page runs until its timeout and then ends with exit code 0; the pairing page also ends once a display is claimed. Ctrl-C ends a page at once, with exit code 130.

Example: reorder the playlist

Open the playlist page for a display, giving yourself two minutes:

Terminal
ledable playlist page --device "$DEVICE" --timeout 120

The first line says the playlist is loading. The next shows it, on one line; here it is indented for reading:

JSON
{
  "state": {
    "load": { "kind": "loaded" },
    "editing": false,
    "changed": false,
    "writing": false,
    "failure": null,
    "apps": [
      {
        "instance_id": "…",
        "name": "Clock",
        "enabled": true,
        "kind": "urlApp"
      },
      {
        "instance_id": "…",
        "name": "Weather",
        "enabled": true,
        "kind": "urlApp"
      },
      {
        "instance_id": "…",
        "name": "Holiday",
        "enabled": true,
        "kind": "dataApp"
      }
    ]
  }
}

kind is urlApp for a store app and dataApp for a picture. To move the picture to the top, type these lines, as you would tap Edit, drag the row and tap Done:

Output
edit
move 2 0
done

After edit, the state has editing: true. After move 2 0 (from position 2 to position 0, counted from 0), the picture is first in apps and changed is true. After done, a line with writing: true follows, and then one with writing: false and failure: null once the new order is stored. If the playlist changed elsewhere in the meantime, failure says so; type force to store your order over it, or discard to drop it. Press Ctrl-C to leave, or let the timeout end the page.

From a program

A program drives a page the same way: read stdout line by line, parse each state, and write a line to stdin when the state shows what you were waiting for. Send edit only once load is loaded, for example, just as a person would wait for the list to appear.

For a single change, the one-shot commands do this for you and exit when the change is stored: playlist move opens the same page, makes the same moves and waits for the write. Several page commands also have a one-shot form of their own: ble link --first, firmware page --update, and wifi join for the Wi‑Fi page.

Several accounts

You can be signed in to more than one account on one computer. Each auth login with another address adds that account and makes it the current one, and every other command acts as the current account, on its displays.

Terminal
ledable auth accounts
ledable auth use <user_id>

auth accounts lists the accounts signed in here, each with its user_id, email and whether it is current. auth use makes another one current. auth logout signs out of the current account only; until you auth use another, no account is current.

What the CLI keeps on your computer

  • A folder in your home directory, .ledable/cli, which on macOS and Linux only your user can read. It holds the accounts signed in here, each account's list of displays, the log lines read so far, cached files and the date of the last update check.
  • Sign-in tokens and the keys that let the CLI talk to your displays over Bluetooth are kept in your system's credential store, such as the Keychain on macOS or the Credential Manager on Windows, never in that folder.

auth logout removes the current account's credentials and its list of displays.

The update notice

At most once a day, the CLI asks npm for its newest version. When there is a newer one, it says so on stderr after the command has finished:

Output
ledable 0.2.0 is available (this is 0.1.0): npm install -g @ledable/cli

It only does this when stderr is a terminal and the CI environment variable is not set, so scripts and CI always see the same output. If npm cannot be reached, there is simply no notice.