Quickstart in Node.js

Een Node.js-programma dat een token vernieuwt, je schermen oplijst en een playlist op een scherm zet. De blokken op deze pagina vormen samen één bestand.

1 Haal een token op

Open de CMS op Instellingen, Verbonden apps en klik op Genereer token. Een lid zonder beheerdersrechten ziet dit tabblad niet.

Kies daarna de organisatie, die bepaalt welke schermen het programma ziet, en klik op Toestaan. De volgende pagina toont de client ID en het refresh token, één keer. Kopieer ze allebei.

Zet in je terminal de client ID en het pad naar het tokenbestand, en schrijf het refresh token naar dat bestand. read -rs wacht zonder iets te tonen: plak het refresh token en druk op Enter, zo blijft het uit de shell-history:

export GIBEON_CLIENT_ID=CLIENT_ID
export GIBEON_REFRESH_TOKEN_FILE=./gibeon-refresh-token
touch "$GIBEON_REFRESH_TOKEN_FILE"
chmod 600 "$GIBEON_REFRESH_TOKEN_FILE"
read -rs GIBEON_REFRESH_TOKEN && printf '%s' "$GIBEON_REFRESH_TOKEN" > "$GIBEON_REFRESH_TOKEN_FILE"

Het programma heeft Node.js 18 of nieuwer nodig, voor de ingebouwde fetch. Er valt niets te installeren. Zet de blokken hieronder in volgorde in gibeon.mjs en draai node gibeon.mjs.

2 Vernieuw het token

Een access token is 60 minuten geldig. Elke vernieuwing geeft ook een nieuw refresh token, dus een waarde in de omgeving is na één run verouderd.

Het programma schrijft het nieuwe token naar een tijdelijk bestand in dezelfde map, zet chmod 600 en hernoemt het over het bestand in GIBEON_REFRESH_TOKEN_FILE. Daarna pas gebruikt het programma het access token. Een proces dat daarbij stopt, houdt zo een werkend refresh token over.

Elke aanvraag controleert de status: bij een fout gooit check een error, en die stopt het programma met een exitcode die niet nul is.

import { randomUUID } from 'node:crypto';
import { chmod, readFile, rename, writeFile } from 'node:fs/promises';

// An uncaught error stops the program with a non-zero exit code.
function check(response) {
  if (!response.ok) {
    throw new Error(`${response.status} from ${response.url}`);
  }
}

const tokenFile = process.env.GIBEON_REFRESH_TOKEN_FILE;
const refreshToken = (await readFile(tokenFile, 'utf8')).trim();

let response = await fetch('https://id.gibeon.io/auth/v1/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'refresh_token',
    client_id: process.env.GIBEON_CLIENT_ID,
    refresh_token: refreshToken,
  }),
  signal: AbortSignal.timeout(30_000),
});
check(response);
const tokens = await response.json();

// Store the new refresh token before anything uses the access token.
const tmpPath = `${tokenFile}.${randomUUID()}.tmp`;
await writeFile(tmpPath, tokens.refresh_token, { mode: 0o600 });
await chmod(tmpPath, 0o600);
await rename(tmpPath, tokenFile);

const headers = { Authorization: `Bearer ${tokens.access_token}` };

3 Lijst je schermen op

Dit geeft alle schermen in je tenant. Het programma print van elk scherm de id, name en status, en nooit een token.

response = await fetch('https://www.gibeon.io/v1/players', {
  headers,
  signal: AbortSignal.timeout(30_000),
});
check(response);

for (const player of (await response.json()).data) {
  console.log(player.id, player.name, player.status);
}

4 Wijs een playlist toe en publiceer

Deze stap draait alleen als GIBEON_PLAYER_ID en GIBEON_PLAYLIST_ID gezet zijn. De id van het scherm staat in de lijst uit de vorige stap. De id van de playlist staat in de adresbalk van de CMS als je de playlist opent: /playlists/PLAYLIST_ID/content. Exporteer ze allebei en draai het programma opnieuw.

Een scherm met zones speelt die; anders zijn sequentie; anders zijn planning; anders zijn eigen playlist. De body maakt de sequentie en de planning leeg, dus de playlist speelt tenzij het scherm zones heeft.

De PATCH alleen verandert niets op het scherm: tot je publiceert, blijft het de oude toewijzing tonen. Publiceren bouwt een nieuwe snapshot en geeft het scherm meteen een seintje.

const playerId = process.env.GIBEON_PLAYER_ID;
const playlistId = process.env.GIBEON_PLAYLIST_ID;

if (playerId && playlistId) {
  response = await fetch(`https://www.gibeon.io/v1/players/${playerId}`, {
    method: 'PATCH',
    headers: { ...headers, 'Content-Type': 'application/json' },
    body: JSON.stringify({ playlist_id: playlistId, sequence_id: null, planning_id: null }),
    signal: AbortSignal.timeout(30_000),
  });
  check(response);
  const player = (await response.json()).data;

  response = await fetch(`https://www.gibeon.io/v1/players/${playerId}/publish`, {
    method: 'POST',
    headers,
    signal: AbortSignal.timeout(30_000),
  });
  check(response);
  console.log(player.id, player.name, player.status);
}

Wat nu