Skip to main content

TomorrowOS Developer Guide

This guide is for developers building a CMS, integrating TomorrowOS into an existing app, or extending the Control Panel on top of @tomorrowos/sdk. It describes what exists today: the Node CMS SDK, the WebSocket / HTTP wire protocol, and the Tizen / BrightSign players — not a future client runtime API. For product-level context, see docs/guides/beginners-guide.md. For the full command list, see docs/api/overview.md.

Requirements

@tomorrowos/sdk declares "engines": { "node": ">=20" }. The generated starters match that floor.

Developer mindset

Check capability first. Execute safely second. Report clearly third.
Screens differ by OS, model, firmware, media engine and permissions. Do not assume reboot, screenshot or on/off timer works until device.info.getCapabilities says so. Do not invent helpers like tomorrow.playback.play() or tomorrow.display.power("off"). Use the SDK instance and dotted wire methods such as device.content.setPolicy.

Architecture

There is no required TomorrowOS cloud. Screens talk to your server.

Scaffold and run

Configure .env (and the same secrets on your host):
Deploy to a host with a persistent Node.js runtime and long-lived WebSockets (Railway, Render, Fly.io, a VPS, etc.). Avoid classic short-lived serverless without WebSocket support. You get:
  • Pairing HTTP (POST /pairing/verify, POST /pairing/unpair)
  • GET /brand.json
  • WebSocket device channel
  • Media upload helpers
  • GET /players/brightsign.zip (CMS URL baked into config.js)
  • Starter Control Panel under staticRoot

Add to an existing app

Keep your auth, tenancy and UI. The SDK adds device sessions, pairing, playlist/policy delivery and optional static helpers.

Install a player and pair

  1. Deploy a public HTTPS CMS URL (or a tunnel while developing).
  2. Control Panel → Download Players → Tizen or BrightSign.
  3. Install on the device:
    • Samsung Tizen — Custom App URL, USB sideload, or Device Manager
    • BrightSign — zip from your CMS embeds cmsEndpoint; copy to SD card root
  4. Player shows a 6-character pairing code.
  5. Control Panel → Pair → submit the code (POST /pairing/verify or tos.pairing.verify(code)).
  6. Device appears online. Upload media, save a playlist, Publish.

Basic flow (shipped)

Typical sequence:
  1. Listen / deploy CMS
  2. Pair device
  3. Read device.info.get + device.info.getCapabilities
  4. Upload / register media
  5. Save playlist
  6. Publish → CMS verify URLs → device.content.setPolicy
  7. Player caches and plays (black-gap-safe handoffs inside the player)
  8. Optional: screenshot, reboot, on/off timer
  9. On failure / empty policy: brand idle fallback

How tos is created

After that:
  • tos.device(deviceId).sendCommand(method, params) — device wire commands
  • tos.pairing.* — pairing
  • tos.playlists.* — playlists and policy publish
  • tos.listDevices() — roster + online state
Name the variable anything; docs use tos as a short alias.

Capability checks

Always check before hardware-facing calls:
Do not treat untested model/firmware as production-safe. Use certification evidence before claiming support.

Information API

device.info.get returns identity / hardware fields (model, firmware, serial, online, …). Use it in the CMS device detail view and for support tickets.

Playback (content policy)

There is no low-level play() RPC per media item. Playback is driven by content policy:
Clear and return to brand idle:
Prefer CMS helpers that verify and build policy:
Or Control Panel: POST /device/{deviceId}/assignments. Wire methods:

Assets and atomic activation

There is no separate tomorrow.assets.* command surface. Flow today:
See docs/guides/assets-and-atomic-activation.md.

Transitions and black gaps

After setPolicy, the player owns image/video handoffs (dual layers, dual AVPlay / dual roVideoPlayer, prefetch, still-mode, etc.). See docs/guides/black-gap-playback.md for image → image, image → video, video → image, video → video, and loop behaviour on Tizen and BrightSign.

Pairing, sync and reconnect

Wire / events you will see:
This sync layer is about connection continuity (reconnect, reboot resume, re-pair, latest policy push) — not frame-accurate video walls. device.ping / device.pong are uplink heartbeats, not an HTTP ping API you call from the Control Panel.

Display and power

Wire methods:
Notes:
  • Timer uses daily HH:mm; turnOnAt / turnOffAt must differ
  • Often model / firmware dependent — check capabilities first
  • There is no shipped device.display.power("off") brightness/volume API today

Telemetry

Screenshot:
Also available via POST /device/{id}/screenshot. Wire method:
Player logs uplink as device.log; read them with GET /device/{id}/logs.

Network / presence

No separate device.network.* command set. Use:

Shipped device command surface

Uplink-only (player → CMS): device.ping, device.log, device.hello, device.resume, …

Error handling

Prefer structured results from sendCommand (ok / error / unsupported) and CMS publish verification failures before policy push. Common field / support issues:
When something fails after a successful publish, players keep last known good media when possible, or show brand idle from GET /brand.json.

Security expectations

  • Do not expose unauthenticated admin routes
  • Do not log secrets or credentials
  • Keep pairing behind your Control Panel auth
  • Treat unknown capability as unsupported until tested
  • Be careful storing / transmitting screenshots
Follow project SECURITY.md where applicable.

Production checklist

  • Exact device model + firmware tested
  • Public HTTPS CMS reachable from the screen network
  • Durable database + media storage (not ephemeral local disk)
  • Publish verification passes for every playlist asset
  • Offline / reboot resume
  • Black-gap transitions for your real media mix (see black-gap guide)
  • Capabilities verified per fleet type
  • Pairing, publish, screenshot, reboot paths exercised end-to-end

Goal

One CMS ↔ player contract, honest capabilities, safe publish and player-owned transitions — without pretending every signage OS is the same. Write against the shipped wire methods. Keep platform details inside the player. Document what you actually certified on real hardware.