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
.env (and the same secrets on your host):
- Pairing HTTP (
POST /pairing/verify,POST /pairing/unpair) GET /brand.json- WebSocket device channel
- Media upload helpers
GET /players/brightsign.zip(CMS URL baked intoconfig.js)- Starter Control Panel under
staticRoot
Add to an existing app
Install a player and pair
- Deploy a public HTTPS CMS URL (or a tunnel while developing).
- Control Panel → Download Players → Tizen or BrightSign.
- 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
- Player shows a 6-character pairing code.
- Control Panel → Pair → submit the code (
POST /pairing/verifyortos.pairing.verify(code)). - Device appears online. Upload media, save a playlist, Publish.
Basic flow (shipped)
- Listen / deploy CMS
- Pair device
- Read
device.info.get+device.info.getCapabilities - Upload / register media
- Save playlist
- Publish → CMS verify URLs →
device.content.setPolicy - Player caches and plays (black-gap-safe handoffs inside the player)
- Optional: screenshot, reboot, on/off timer
- On failure / empty policy: brand idle fallback
How tos is created
tos.device(deviceId).sendCommand(method, params)— device wire commandstos.pairing.*— pairingtos.playlists.*— playlists and policy publishtos.listDevices()— roster + online state
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-levelplay() RPC per media item.
Playback is driven by content policy:
POST /device/{deviceId}/assignments.
Wire methods:
Assets and atomic activation
There is no separatetomorrow.assets.* command surface.
Flow today:
docs/guides/assets-and-atomic-activation.md.
Transitions and black gaps
AftersetPolicy, 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
device.ping / device.pong are uplink heartbeats, not an HTTP ping API you call from the Control Panel.
Display and power
- Timer uses daily
HH:mm;turnOnAt/turnOffAtmust differ - Often model / firmware dependent — check capabilities first
- There is no shipped
device.display.power("off")brightness/volume API today
Telemetry
Screenshot:POST /device/{id}/screenshot.
Wire method:
device.log; read them with GET /device/{id}/logs.
Network / presence
No separatedevice.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 fromsendCommand (ok / error / unsupported) and CMS publish verification failures before policy push.
Common field / support issues:
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
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