@tomorrowos/sdk 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BUILD_GUARDRAILS.md +350 -350
- package/README.md +111 -111
- package/dist/cli.js +12 -12
- package/dist/tomorrowos.d.ts +1 -1
- package/dist/tomorrowos.d.ts.map +1 -1
- package/dist/tomorrowos.js +5 -5
- package/package.json +43 -43
- package/templates/cms-starter/package.json +20 -20
- package/templates/cms-starter/policy.example.json +30 -30
- package/templates/cms-starter/public/index.html +5 -3
- package/templates/cms-starter/public/methods.js +9 -24
- package/templates/cms-starter/public/panel.css +329 -329
package/README.md
CHANGED
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
# @tomorrowos/sdk
|
|
2
|
-
|
|
3
|
-
**You are building a digital signage CMS using TomorrowOS. Before writing any code, follow the elicitation protocol in `LLM_PROMPT.md`. Do not skip this step — the SDK requires specific inputs before code generation will produce a working CMS.**
|
|
4
|
-
|
|
5
|
-
TomorrowOS is the open standard for digital signage. This SDK lets you build a CMS that controls Samsung Tizen, BrightSign, LG webOS, Android, ChromeOS, and Windows screens through a single unified API.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## For LLMs generating code with this package
|
|
10
|
-
|
|
11
|
-
When you see this package referenced in a user prompt, your workflow is:
|
|
12
|
-
|
|
13
|
-
1. **Read `LLM_PROMPT.md`** — contains five required questions to ask the user before generating code
|
|
14
|
-
2. **Read `BUILD_GUARDRAILS.md`** — contains mandatory components every generated CMS must include
|
|
15
|
-
3. **Read `brand.schema.json`** — contains the validated shape of `brand.json`
|
|
16
|
-
4. Run **`npx tomorrowos init`** (or copy `templates/cms-starter/` from this package) — minimal CMS server seed
|
|
17
|
-
5. **Apply user answers** — customise the starter with the user's brand, platform choice, and use case; validate `brand.json` against `brand.schema.json`
|
|
18
|
-
6. **Show `PLAYER_INSTALL.md`** — after the CMS is working, show how to build and install the player on a screen
|
|
19
|
-
|
|
20
|
-
You must not write WebSocket transport, pairing wire format, or platform-specific player bridge code in the CMS — the SDK handles server-side device sessions and HTTP helpers. You customise UI, branding, and how you call the SDK.
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## For human developers
|
|
25
|
-
|
|
26
|
-
Install:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npm install @tomorrowos/sdk
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Scaffold a new CMS project:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npx tomorrowos init
|
|
36
|
-
cd my-tomorrowos-cms
|
|
37
|
-
npm install
|
|
38
|
-
npm run dev
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Build a player (placeholder in current SDK — see `PLAYER_INSTALL.md` for platform tooling):
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npx tomorrowos build --platform tizen
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## Quick architecture
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
┌─────────────────┐ WebSocket ┌─────────────────┐
|
|
53
|
-
│ Your CMS │ ◄────────────────────────►│ TomorrowOS │
|
|
54
|
-
│ (your server) │ │ Player on │
|
|
55
|
-
│ │ │ screen │
|
|
56
|
-
│ imports │ │ │
|
|
57
|
-
│ @tomorrowos │ │ unified API │
|
|
58
|
-
│ /sdk │ │ across OSes │
|
|
59
|
-
└─────────────────┘ └─────────────────┘
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
TomorrowOS has no required cloud service. Your CMS talks to your screens over your network.
|
|
63
|
-
|
|
64
|
-
**Player branding:** `GET /brand.json` returns the `brand` object passed to `new TomorrowOS({ brand })` (same host/port as `listen`). TomorrowOS players can fetch it to apply `backgroundColor`, logos, etc., without a separate static file server.
|
|
65
|
-
|
|
66
|
-
**Content policy (`device.content.setPolicy`):** POST `/device/{deviceId}/content/set-policy` with `{ "policy": { "playlists": [...], "fallback": { "type": "brand" } } }`. Each playlist may include optional `schedule` (device local time):
|
|
67
|
-
|
|
68
|
-
| Field | Format | Notes |
|
|
69
|
-
|-------|--------|--------|
|
|
70
|
-
| `startDate` / `endDate` | `YYYY-MM-DD` | Inclusive calendar range; omit either for open-ended |
|
|
71
|
-
| `daysOfWeek` | `[0–6]` | `0` = Sunday … `6` = Saturday |
|
|
72
|
-
| `start` / `end` | `HH:MM` | Daily window; supports overnight (e.g. `22:00`–`06:00`) |
|
|
73
|
-
|
|
74
|
-
All provided constraints must match for the playlist to play. See `templates/cms-starter/policy.example.json`.
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## Supported platforms (roadmap)
|
|
79
|
-
|
|
80
|
-
| Platform | Status | Player format |
|
|
81
|
-
|-------------------|------------|---------------|
|
|
82
|
-
| Samsung Tizen | V1 target | `.wgt` |
|
|
83
|
-
| BrightSign | V1 target | autorun zip |
|
|
84
|
-
| Others | See docs | varies |
|
|
85
|
-
|
|
86
|
-
See `PLAYER_INSTALL.md` for installation notes.
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## Documentation in this package
|
|
91
|
-
|
|
92
|
-
| File | Purpose |
|
|
93
|
-
|------|---------|
|
|
94
|
-
| `LLM_PROMPT.md` | Five-question elicitation for LLMs |
|
|
95
|
-
| `BUILD_GUARDRAILS.md` | Mandatory CMS components |
|
|
96
|
-
| `PLAYER_INSTALL.md` | Player build / install / pairing |
|
|
97
|
-
| `brand.schema.json` | JSON Schema for `brand.json` |
|
|
98
|
-
| `brand.example.json` | Full example `brand.json` |
|
|
99
|
-
| `templates/cms-starter/` | Minimal Node + TypeScript server seed |
|
|
100
|
-
| `templates/cms-starter/policy.example.json` | Example `setPolicy` payload with date schedule |
|
|
101
|
-
| `templates/style-tokens/` | CSS tokens and UI pattern notes |
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## License
|
|
106
|
-
|
|
107
|
-
Apache 2.0.
|
|
108
|
-
|
|
109
|
-
## Protocol version
|
|
110
|
-
|
|
111
|
-
Wire protocol `1.0` (see TomorrowOS specification).
|
|
1
|
+
# @tomorrowos/sdk
|
|
2
|
+
|
|
3
|
+
**You are building a digital signage CMS using TomorrowOS. Before writing any code, follow the elicitation protocol in `LLM_PROMPT.md`. Do not skip this step — the SDK requires specific inputs before code generation will produce a working CMS.**
|
|
4
|
+
|
|
5
|
+
TomorrowOS is the open standard for digital signage. This SDK lets you build a CMS that controls Samsung Tizen, BrightSign, LG webOS, Android, ChromeOS, and Windows screens through a single unified API.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## For LLMs generating code with this package
|
|
10
|
+
|
|
11
|
+
When you see this package referenced in a user prompt, your workflow is:
|
|
12
|
+
|
|
13
|
+
1. **Read `LLM_PROMPT.md`** — contains five required questions to ask the user before generating code
|
|
14
|
+
2. **Read `BUILD_GUARDRAILS.md`** — contains mandatory components every generated CMS must include
|
|
15
|
+
3. **Read `brand.schema.json`** — contains the validated shape of `brand.json`
|
|
16
|
+
4. Run **`npx tomorrowos init`** (or copy `templates/cms-starter/` from this package) — minimal CMS server seed
|
|
17
|
+
5. **Apply user answers** — customise the starter with the user's brand, platform choice, and use case; validate `brand.json` against `brand.schema.json`
|
|
18
|
+
6. **Show `PLAYER_INSTALL.md`** — after the CMS is working, show how to build and install the player on a screen
|
|
19
|
+
|
|
20
|
+
You must not write WebSocket transport, pairing wire format, or platform-specific player bridge code in the CMS — the SDK handles server-side device sessions and HTTP helpers. You customise UI, branding, and how you call the SDK.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## For human developers
|
|
25
|
+
|
|
26
|
+
Install:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @tomorrowos/sdk
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Scaffold a new CMS project:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx tomorrowos init
|
|
36
|
+
cd my-tomorrowos-cms
|
|
37
|
+
npm install
|
|
38
|
+
npm run dev
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Build a player (placeholder in current SDK — see `PLAYER_INSTALL.md` for platform tooling):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx tomorrowos build --platform tizen
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Quick architecture
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
┌─────────────────┐ WebSocket ┌─────────────────┐
|
|
53
|
+
│ Your CMS │ ◄────────────────────────►│ TomorrowOS │
|
|
54
|
+
│ (your server) │ │ Player on │
|
|
55
|
+
│ │ │ screen │
|
|
56
|
+
│ imports │ │ │
|
|
57
|
+
│ @tomorrowos │ │ unified API │
|
|
58
|
+
│ /sdk │ │ across OSes │
|
|
59
|
+
└─────────────────┘ └─────────────────┘
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
TomorrowOS has no required cloud service. Your CMS talks to your screens over your network.
|
|
63
|
+
|
|
64
|
+
**Player branding:** `GET /brand.json` returns the `brand` object passed to `new TomorrowOS({ brand })` (same host/port as `listen`). TomorrowOS players can fetch it to apply `backgroundColor`, logos, etc., without a separate static file server.
|
|
65
|
+
|
|
66
|
+
**Content policy (`device.content.setPolicy`):** POST `/device/{deviceId}/content/set-policy` with `{ "policy": { "playlists": [...], "fallback": { "type": "brand" } } }`. Each playlist may include optional `schedule` (device local time):
|
|
67
|
+
|
|
68
|
+
| Field | Format | Notes |
|
|
69
|
+
|-------|--------|--------|
|
|
70
|
+
| `startDate` / `endDate` | `YYYY-MM-DD` | Inclusive calendar range; omit either for open-ended |
|
|
71
|
+
| `daysOfWeek` | `[0–6]` | `0` = Sunday … `6` = Saturday |
|
|
72
|
+
| `start` / `end` | `HH:MM` | Daily window; supports overnight (e.g. `22:00`–`06:00`) |
|
|
73
|
+
|
|
74
|
+
All provided constraints must match for the playlist to play. See `templates/cms-starter/policy.example.json`.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Supported platforms (roadmap)
|
|
79
|
+
|
|
80
|
+
| Platform | Status | Player format |
|
|
81
|
+
|-------------------|------------|---------------|
|
|
82
|
+
| Samsung Tizen | V1 target | `.wgt` |
|
|
83
|
+
| BrightSign | V1 target | autorun zip |
|
|
84
|
+
| Others | See docs | varies |
|
|
85
|
+
|
|
86
|
+
See `PLAYER_INSTALL.md` for installation notes.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Documentation in this package
|
|
91
|
+
|
|
92
|
+
| File | Purpose |
|
|
93
|
+
|------|---------|
|
|
94
|
+
| `LLM_PROMPT.md` | Five-question elicitation for LLMs |
|
|
95
|
+
| `BUILD_GUARDRAILS.md` | Mandatory CMS components |
|
|
96
|
+
| `PLAYER_INSTALL.md` | Player build / install / pairing |
|
|
97
|
+
| `brand.schema.json` | JSON Schema for `brand.json` |
|
|
98
|
+
| `brand.example.json` | Full example `brand.json` |
|
|
99
|
+
| `templates/cms-starter/` | Minimal Node + TypeScript server seed |
|
|
100
|
+
| `templates/cms-starter/policy.example.json` | Example `setPolicy` payload with date schedule |
|
|
101
|
+
| `templates/style-tokens/` | CSS tokens and UI pattern notes |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
Apache 2.0.
|
|
108
|
+
|
|
109
|
+
## Protocol version
|
|
110
|
+
|
|
111
|
+
Wire protocol `1.0` (see TomorrowOS specification).
|
package/dist/cli.js
CHANGED
|
@@ -56,18 +56,18 @@ function cmdBuild(argv) {
|
|
|
56
56
|
process.exitCode = 0;
|
|
57
57
|
}
|
|
58
58
|
function printHelp() {
|
|
59
|
-
console.log(`tomorrowos — TomorrowOS SDK CLI
|
|
60
|
-
|
|
61
|
-
Usage:
|
|
62
|
-
tomorrowos init [directory] Copy cms-starter template (default: my-tomorrowos-cms)
|
|
63
|
-
tomorrowos build --platform … Placeholder; player packaging lives in the player repo
|
|
64
|
-
|
|
65
|
-
Options:
|
|
66
|
-
--force With init: copy into a non-empty directory
|
|
67
|
-
|
|
68
|
-
Examples:
|
|
69
|
-
npx @tomorrowos/sdk init
|
|
70
|
-
npx @tomorrowos/sdk init ./my-cms
|
|
59
|
+
console.log(`tomorrowos — TomorrowOS SDK CLI
|
|
60
|
+
|
|
61
|
+
Usage:
|
|
62
|
+
tomorrowos init [directory] Copy cms-starter template (default: my-tomorrowos-cms)
|
|
63
|
+
tomorrowos build --platform … Placeholder; player packaging lives in the player repo
|
|
64
|
+
|
|
65
|
+
Options:
|
|
66
|
+
--force With init: copy into a non-empty directory
|
|
67
|
+
|
|
68
|
+
Examples:
|
|
69
|
+
npx @tomorrowos/sdk init
|
|
70
|
+
npx @tomorrowos/sdk init ./my-cms
|
|
71
71
|
`);
|
|
72
72
|
}
|
|
73
73
|
function main() {
|
package/dist/tomorrowos.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export interface DeviceListItem {
|
|
|
40
40
|
lastPolicyPushAt: string | null;
|
|
41
41
|
screenOnlineActive: boolean;
|
|
42
42
|
screenOnlineLabel: string;
|
|
43
|
-
/**
|
|
43
|
+
/** Same as lastBootAt while connected; used for uptime display. */
|
|
44
44
|
screenOnlineSince: string | null;
|
|
45
45
|
}
|
|
46
46
|
export declare class TomorrowOS extends EventEmitter {
|
package/dist/tomorrowos.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,IAAI,MAAM,MAAM,CAAC;AAKxB,OAAO,KAAK,EAAsB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAG5E,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAsBD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,
|
|
1
|
+
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.js
CHANGED
|
@@ -153,14 +153,14 @@ export class TomorrowOS extends EventEmitter {
|
|
|
153
153
|
const connected = this.isDeviceConnected(deviceId);
|
|
154
154
|
let screenOnlineActive = false;
|
|
155
155
|
let screenOnlineLabel = "Not active";
|
|
156
|
-
if (connected && record.
|
|
157
|
-
const
|
|
158
|
-
if (!Number.isNaN(
|
|
156
|
+
if (connected && record.lastBootAt) {
|
|
157
|
+
const bootMs = new Date(record.lastBootAt).getTime();
|
|
158
|
+
if (!Number.isNaN(bootMs)) {
|
|
159
159
|
screenOnlineActive = true;
|
|
160
|
-
screenOnlineLabel = formatDurationMs(now -
|
|
160
|
+
screenOnlineLabel = formatDurationMs(now - bootMs);
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
-
const screenOnlineSince = connected && record.
|
|
163
|
+
const screenOnlineSince = connected && record.lastBootAt ? record.lastBootAt : null;
|
|
164
164
|
return {
|
|
165
165
|
deviceId,
|
|
166
166
|
connected,
|
package/package.json
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "TomorrowOS CMS server SDK — WebSocket transport, pairing, device commands, optional static CMS UI. Includes CLI (tomorrowos init / build) and starter templates.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/index.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"bin": {
|
|
15
|
-
"tomorrowos": "./dist/cli.js"
|
|
16
|
-
},
|
|
17
|
-
"files": [
|
|
18
|
-
"dist",
|
|
19
|
-
"templates",
|
|
20
|
-
"README.md",
|
|
21
|
-
"LLM_PROMPT.md",
|
|
22
|
-
"BUILD_GUARDRAILS.md",
|
|
23
|
-
"PLAYER_INSTALL.md",
|
|
24
|
-
"brand.schema.json",
|
|
25
|
-
"brand.example.json"
|
|
26
|
-
],
|
|
27
|
-
"scripts": {
|
|
28
|
-
"build": "tsc",
|
|
29
|
-
"prepublishOnly": "npm run build"
|
|
30
|
-
},
|
|
31
|
-
"engines": {
|
|
32
|
-
"node": ">=18"
|
|
33
|
-
},
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"ws": "^8.18.0"
|
|
36
|
-
},
|
|
37
|
-
"devDependencies": {
|
|
38
|
-
"@types/node": "^20.19.41",
|
|
39
|
-
"@types/ws": "^8.5.10",
|
|
40
|
-
"typescript": "^5.5.0"
|
|
41
|
-
},
|
|
42
|
-
"license": "Apache-2.0"
|
|
43
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@tomorrowos/sdk",
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "TomorrowOS CMS server SDK — WebSocket transport, pairing, device commands, optional static CMS UI. Includes CLI (tomorrowos init / build) and starter templates.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"tomorrowos": "./dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"templates",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LLM_PROMPT.md",
|
|
22
|
+
"BUILD_GUARDRAILS.md",
|
|
23
|
+
"PLAYER_INSTALL.md",
|
|
24
|
+
"brand.schema.json",
|
|
25
|
+
"brand.example.json"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"ws": "^8.18.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^20.19.41",
|
|
39
|
+
"@types/ws": "^8.5.10",
|
|
40
|
+
"typescript": "^5.5.0"
|
|
41
|
+
},
|
|
42
|
+
"license": "Apache-2.0"
|
|
43
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "my-cms",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
|
|
5
|
-
"private": true,
|
|
6
|
-
"type": "module",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"dev": "tsx watch server.ts",
|
|
9
|
-
"start": "tsx server.ts",
|
|
10
|
-
"build-player": "tomorrowos build --platform tizen"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@tomorrowos/sdk": "^0.2.
|
|
14
|
-
},
|
|
15
|
-
"devDependencies": {
|
|
16
|
-
"@types/node": "^20.0.0",
|
|
17
|
-
"tsx": "^4.19.0",
|
|
18
|
-
"typescript": "^5.5.0"
|
|
19
|
-
}
|
|
20
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "my-cms",
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "tsx watch server.ts",
|
|
9
|
+
"start": "tsx server.ts",
|
|
10
|
+
"build-player": "tomorrowos build --platform tizen"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@tomorrowos/sdk": "^0.2.4"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/node": "^20.0.0",
|
|
17
|
+
"tsx": "^4.19.0",
|
|
18
|
+
"typescript": "^5.5.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
{
|
|
2
|
-
"policy": {
|
|
3
|
-
"playlists": [
|
|
4
|
-
{
|
|
5
|
-
"id": "weekday-promo",
|
|
6
|
-
"name": "Weekday promo",
|
|
7
|
-
"schedule": {
|
|
8
|
-
"startDate": "2026-05-01",
|
|
9
|
-
"endDate": "2026-05-31",
|
|
10
|
-
"daysOfWeek": [1, 2, 3, 4, 5],
|
|
11
|
-
"start": "09:00",
|
|
12
|
-
"end": "17:00"
|
|
13
|
-
},
|
|
14
|
-
"items": [
|
|
15
|
-
{
|
|
16
|
-
"url": "https://example.com/hero.jpg",
|
|
17
|
-
"type": "image",
|
|
18
|
-
"durationMs": 10000
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"url": "https://example.com/spot.mp4",
|
|
22
|
-
"type": "video",
|
|
23
|
-
"durationMs": 30000
|
|
24
|
-
}
|
|
25
|
-
]
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
"fallback": { "type": "brand" }
|
|
29
|
-
}
|
|
30
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"policy": {
|
|
3
|
+
"playlists": [
|
|
4
|
+
{
|
|
5
|
+
"id": "weekday-promo",
|
|
6
|
+
"name": "Weekday promo",
|
|
7
|
+
"schedule": {
|
|
8
|
+
"startDate": "2026-05-01",
|
|
9
|
+
"endDate": "2026-05-31",
|
|
10
|
+
"daysOfWeek": [1, 2, 3, 4, 5],
|
|
11
|
+
"start": "09:00",
|
|
12
|
+
"end": "17:00"
|
|
13
|
+
},
|
|
14
|
+
"items": [
|
|
15
|
+
{
|
|
16
|
+
"url": "https://example.com/hero.jpg",
|
|
17
|
+
"type": "image",
|
|
18
|
+
"durationMs": 10000
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"url": "https://example.com/spot.mp4",
|
|
22
|
+
"type": "video",
|
|
23
|
+
"durationMs": 30000
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"fallback": { "type": "brand" }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -30,11 +30,13 @@
|
|
|
30
30
|
</div>
|
|
31
31
|
</section>
|
|
32
32
|
|
|
33
|
-
<section class="card">
|
|
33
|
+
<section class="card" id="cmsUrlSection">
|
|
34
34
|
<h2>CMS URL for screens</h2>
|
|
35
35
|
<p class="hint">
|
|
36
|
-
|
|
37
|
-
<code>
|
|
36
|
+
<strong>Local development only.</strong> Fill this in when you run the CMS on your PC and
|
|
37
|
+
TVs are on the same LAN (e.g. <code>http://192.168.1.105:3000</code> — same machine as your
|
|
38
|
+
<code>ws://</code> address, not <code>localhost</code>). On hosted CMS (Replit, etc.) you
|
|
39
|
+
normally do <strong>not</strong> need to change this.
|
|
38
40
|
</p>
|
|
39
41
|
<div class="row">
|
|
40
42
|
<input
|
|
@@ -9,7 +9,6 @@ let playlistItems = [];
|
|
|
9
9
|
let devicesCache = [];
|
|
10
10
|
|
|
11
11
|
let devicePollTimer = null;
|
|
12
|
-
let deviceUptimeTimer = null;
|
|
13
12
|
|
|
14
13
|
function escapeHtml(value) {
|
|
15
14
|
return String(value ?? "")
|
|
@@ -48,24 +47,14 @@ function formatDateTimeSeconds(iso) {
|
|
|
48
47
|
});
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
/** TV
|
|
50
|
+
/** TV uptime: now minus last boot time (device.lastBootAt). Not active when app is offline. */
|
|
52
51
|
function formatDeviceOnlineLabel(device) {
|
|
53
52
|
if (!device.connected) return "Not active";
|
|
54
|
-
const
|
|
55
|
-
if (!
|
|
56
|
-
const
|
|
57
|
-
if (Number.isNaN(
|
|
58
|
-
return formatDurationMs(Date.now() -
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function updateDeviceUptimeLabels() {
|
|
62
|
-
document.querySelectorAll(".device-card[data-device-id]").forEach((card) => {
|
|
63
|
-
const deviceId = card.dataset.deviceId;
|
|
64
|
-
const device = devicesCache.find((d) => d.deviceId === deviceId);
|
|
65
|
-
if (!device) return;
|
|
66
|
-
const el = card.querySelector(".device-online-time");
|
|
67
|
-
if (el) el.textContent = formatDeviceOnlineLabel(device);
|
|
68
|
-
});
|
|
53
|
+
const bootIso = device.lastBootAt;
|
|
54
|
+
if (!bootIso) return "Not active";
|
|
55
|
+
const bootMs = new Date(bootIso).getTime();
|
|
56
|
+
if (Number.isNaN(bootMs)) return "Not active";
|
|
57
|
+
return formatDurationMs(Date.now() - bootMs);
|
|
69
58
|
}
|
|
70
59
|
|
|
71
60
|
async function fetchDevices() {
|
|
@@ -136,8 +125,7 @@ function renderDeviceCards() {
|
|
|
136
125
|
const dd = document.createElement("dd");
|
|
137
126
|
if (label === "Device online") {
|
|
138
127
|
dd.className = "device-online-time";
|
|
139
|
-
dd.title =
|
|
140
|
-
"Time since this TV's WebSocket session became active (updates every minute)";
|
|
128
|
+
dd.title = "Current time minus this TV last boot time";
|
|
141
129
|
}
|
|
142
130
|
dd.textContent = value;
|
|
143
131
|
row.appendChild(dt);
|
|
@@ -198,14 +186,11 @@ function renderDeviceCards() {
|
|
|
198
186
|
|
|
199
187
|
function startDevicePolling() {
|
|
200
188
|
if (devicePollTimer) clearInterval(devicePollTimer);
|
|
201
|
-
if (deviceUptimeTimer) clearInterval(deviceUptimeTimer);
|
|
202
189
|
|
|
203
190
|
void fetchDevices();
|
|
204
191
|
devicePollTimer = setInterval(() => {
|
|
205
192
|
void fetchDevices();
|
|
206
193
|
}, 8000);
|
|
207
|
-
|
|
208
|
-
deviceUptimeTimer = setInterval(updateDeviceUptimeLabels, 60_000);
|
|
209
194
|
}
|
|
210
195
|
|
|
211
196
|
function showResult(data) {
|
|
@@ -267,7 +252,7 @@ function absoluteMediaUrl(path) {
|
|
|
267
252
|
const base = getMediaBaseOrigin();
|
|
268
253
|
if (!base) {
|
|
269
254
|
throw new Error(
|
|
270
|
-
"
|
|
255
|
+
"Local CMS only: set CMS URL for screens (your PC LAN IP, e.g. http://192.168.1.105:3000 — not localhost)."
|
|
271
256
|
);
|
|
272
257
|
}
|
|
273
258
|
return `${base}${p.startsWith("/") ? p : `/${p}`}`;
|
|
@@ -558,7 +543,7 @@ async function publishToDevice(deviceId) {
|
|
|
558
543
|
const mediaBase = getMediaBaseOrigin();
|
|
559
544
|
if (!mediaBase) {
|
|
560
545
|
alert(
|
|
561
|
-
"
|
|
546
|
+
"Local CMS only: set CMS URL for screens first (e.g. http://192.168.1.105:3000 — your PC LAN IP, not localhost)."
|
|
562
547
|
);
|
|
563
548
|
return;
|
|
564
549
|
}
|