> ## Documentation Index
> Fetch the complete documentation index at: https://docs.visualradioassist.live/llms.txt
> Use this file to discover all available pages before exploring further.

# SKAARHOJ

> Control the Core from a SKAARHOJ panel through the HTTP device core of Blue Pill and Reactor, with feedback from the plain-text routes.

SKAARHOJ panels with a Blue Pill inside run Reactor, which can talk to any HTTP API through its generic HTTP device core. That core sends GET and POST requests with headers and HTTP Basic or Digest authentication, and reads feedback from the response body with a regular expression. That fits the Core API's [plain-text routes](/develop-with-vra/core-control-api/plain-text-routes): each answers one bare value such as `onair` or `1`.

<Note>
  The Core generates ready-made parameter files for your studio: download `http://192.168.1.50:3002/api/v2/presets/skaarhoj?format=zip` (authenticated like any other read) and import the `.json` files into the HTTP Device Core. One file per key: studio and Core toggles, every writable signal, macro, camera cut / preview / tally, angle, output slot and variable, plus a `README.txt`. The sections below explain what those files contain, for hand-made parameters.
</Note>

## Create a device token

Open **Studio settings → Core API** in VRA Cloud, click **Add device** and name it, for example `SKAARHOJ desk`. Copy the **SKAARHOJ URL**. It looks like this:

```text theme={null}
http://device:vra_examp1etoken…@192.168.1.50:3002/api/v2/plain/studio/status
```

The token is the password of HTTP Basic authentication; the username `device` can be anything. You can also enter a username and the token as password in the authentication settings of the device core instead of in the URL.

Panels often have a fixed IP address. Set it as **Fixed IP address** on the device, so the token only works from the panel. Or skip the token: add the panel's address under **Access without a token → Listed devices**. See [Authentication and LAN access](/develop-with-vra/core-control-api/authentication-and-lan-access).

## Base URL

Point the HTTP device core at the Core, with the credentials in the URL when you do not set them separately:

```text theme={null}
http://device:vra_examp1etoken…@192.168.1.50:3002
```

All paths below are relative to it.

## Commands

Every command is a GET. Control routes answer `OK` on success and `ERR <code>` on failure.

| Command | Path |
| - | - |
| Studio on air | `/api/v2/plain/studio/onair` |
| Studio off air | `/api/v2/plain/studio/offair` |
| Studio recording | `/api/v2/plain/studio/recording` |
| Core activate | `/api/v2/plain/core/activate` |
| Core deactivate | `/api/v2/plain/core/deactivate` |
| Audio Director on | `/api/v2/plain/audio-director/on` |
| Audio Director off | `/api/v2/plain/audio-director/off` |
| Camera Assist on | `/api/v2/plain/camera-assist/on` |
| Camera Assist off | `/api/v2/plain/camera-assist/off` |
| Signal on | `/api/v2/plain/signals/break-lamp/on` |
| Signal off | `/api/v2/plain/signals/break-lamp/off` |
| Trigger a macro | `/api/v2/plain/macros/Jingle%20in/trigger` |
| Take a scene on an output | `/api/v2/plain/outputs/main/scenes/weather/take` |
| Rundown next on an output | `/api/v2/plain/outputs/main/rundown/next` |
| Cut to camera 2 | `/api/v2/plain/cameras/2/cut` |
| Trigger an angle | `/api/v2/plain/cameras/2/angles/Guest%20close/trigger` |
| Variable on | `/api/v2/plain/variables/live/on` |
| Variable off | `/api/v2/plain/variables/live/off` |

`break-lamp` stands for one of your signal identifiers. Macros are addressed by id or name. `main` stands for the slot key of an output and `weather` for one of its scenes; add `?scene=<playout scene key>` to the rundown path when the output has more than one playout scene. `2` is a camera number (a camera name or id works too) and `Guest close` one of its angles. `live` stands for a BOOLEAN user input variable of the station; variable writes go through VRA Cloud and need the Core's internet connection. See [Routes](/develop-with-vra/core-control-api/routes) for all routes.

### On/off buttons

For a button that switches something on and off, use an A/B toggle with two commands: A sends `/on`, B sends `/off`. For the studio, A sends `/studio/onair` and B sends `/studio/offair`. For a BOOLEAN variable, A sends `/variables/live/on` and B sends `/variables/live/off`.

The explicit routes are idempotent. When the panel's idea of the state is out of date, `/on` on a signal that is already on simply answers `OK`. A `/toggle` route would flip it the wrong way.

## Feedback

The HTTP device core polls a path and runs one regular expression with one capture group on the response body. The captured text is the value of the parameter.

| Parameter | Path | Regular expression |
| - | - | - |
| Studio status | `/api/v2/plain/studio/status` | `^(onair\|offair\|recording)$` |
| Core active | `/api/v2/plain/core/active` | `^(true\|false)$` |
| Core state | `/api/v2/plain/core/state` | `^([A-Z_]+)$` |
| Audio Director | `/api/v2/plain/audio-director` | `^([01])$` |
| Camera Assist | `/api/v2/plain/camera-assist` | `^([01])$` |
| A signal | `/api/v2/plain/signals/break-lamp` | `^([01])$` |
| Signal value | `/api/v2/plain/signals/break-lamp/value` | `^(.*)$` |
| Macro running | `/api/v2/plain/macros/Jingle%20in/active` | `^([01])$` |
| Program on air | `/api/v2/plain/station/program/title` | `^(.*)$` |
| Output on air | `/api/v2/plain/outputs/main/status` | `^(on_air)$` |
| Output status | `/api/v2/plain/outputs/main/status` | `^(starting\|on_air\|dormant\|fault\|offline)$` |
| Scene on air | `/api/v2/plain/outputs/main/scene` | `^(.*)$` |
| Camera 2 tally lamp | `/api/v2/plain/cameras/2/on-air` | `^(1)$` |
| Camera on air | `/api/v2/plain/cameras` | `^([0-9]+)$` |
| Variable on | `/api/v2/plain/variables/live` | `^(1)$` |

* The plain body has no trailing newline, so `^` and `$` match the whole value.
* An error answers `ERR <code>`, which the expressions with fixed values do not match. The parameter then keeps no value instead of showing a wrong one. `^(.*)$` matches an error too.
* A camera tally is `1` on air, `0` off air and empty when the Core cannot tell. `^(1)$` keeps the tally lamp off in the last two cases.
* Poll every 1 to 3 seconds. Reactor polls in whole seconds.
* Several values from one call? `GET /api/v2/state` returns them all as JSON, but one regular expression per parameter on a JSON body is fragile. One plain route per parameter is easier to keep right.

## Advanced model

Reactor's **Advanced model** describes the parameters of a generic device, and can import parameter definitions from JSON files. The files from `GET /api/v2/presets/skaarhoj?format=zip` are made for this import (`?format=json-files` returns them as one JSON object instead). They follow SKAARHOJ's HTTP Device Core parameter format: `Toggle` parameters call `/api/v2/plain/…/{value}` with `OnVal`/`OffVal`, `Trigger` parameters call one route, and read parameters use `Regex match value` with one capture group on the plain answer.

## Troubleshooting

Open the URL in a browser on the same network, with `/api/v2/...` instead of `/api/v2/plain/...`. The JSON answer names the error, for example `"error":"unauthorized"` for a wrong token. On the Core machine, `http://localhost:3002/api/v2/requests` lists the last calls with the panel's address, status and error code. See [Errors and status codes](/develop-with-vra/core-control-api/errors-and-status-codes).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.