@tomorrowos/sdk 0.3.7 → 0.3.9
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/dist/tomorrowos.d.ts.map +1 -1
- package/dist/tomorrowos.js +29 -3
- package/package.json +1 -1
- package/templates/cms-starter/brand.json +23 -23
- package/templates/cms-starter/package.json +2 -2
- package/templates/cms-starter/public/index.html +1 -1
- package/templates/cms-starter/public/methods.js +17 -5
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;AAUxB,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAIV,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAO1B,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;
|
|
1
|
+
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.js
CHANGED
|
@@ -32,6 +32,29 @@ function parseBootIsoMs(iso) {
|
|
|
32
32
|
const ms = new Date(iso).getTime();
|
|
33
33
|
return Number.isFinite(ms) ? ms : null;
|
|
34
34
|
}
|
|
35
|
+
function parseBootUptimeSec(msg) {
|
|
36
|
+
const raw = msg.bootUptimeSec ?? msg.bootUptimeSeconds;
|
|
37
|
+
if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0)
|
|
38
|
+
return raw;
|
|
39
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
40
|
+
const n = Number(raw);
|
|
41
|
+
if (Number.isFinite(n) && n >= 0)
|
|
42
|
+
return n;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
/** Derive boot instant from device uptime + CMS clock (avoids wrong TV wall clock). */
|
|
47
|
+
function bootAtFromUptimeSec(uptimeSec) {
|
|
48
|
+
return new Date(Date.now() - uptimeSec * 1000).toISOString();
|
|
49
|
+
}
|
|
50
|
+
function lastBootAtFromHandshake(msg, existing) {
|
|
51
|
+
const uptimeSec = parseBootUptimeSec(msg);
|
|
52
|
+
if (uptimeSec != null) {
|
|
53
|
+
return resolveLastBootAt(bootAtFromUptimeSec(uptimeSec), existing);
|
|
54
|
+
}
|
|
55
|
+
const bootedAt = typeof msg.bootedAt === "string" ? msg.bootedAt : undefined;
|
|
56
|
+
return resolveLastBootAt(bootedAt, existing);
|
|
57
|
+
}
|
|
35
58
|
/** Merge device-reported boot time with stored value (ignore page-reload noise, accept reboots). */
|
|
36
59
|
function resolveLastBootAt(incoming, existing) {
|
|
37
60
|
const inMs = parseBootIsoMs(incoming);
|
|
@@ -168,7 +191,9 @@ export class TomorrowOS extends EventEmitter {
|
|
|
168
191
|
const pairedAt = new Date().toISOString();
|
|
169
192
|
const meta = this.pendingDeviceMeta.get(deviceId);
|
|
170
193
|
const now = pairedAt;
|
|
171
|
-
const lastBootAt =
|
|
194
|
+
const lastBootAt = meta?.bootUptimeSec != null
|
|
195
|
+
? resolveLastBootAt(bootAtFromUptimeSec(meta.bootUptimeSec), undefined)
|
|
196
|
+
: resolveLastBootAt(meta?.bootedAt, undefined);
|
|
172
197
|
await this.store.setPairedDevice(deviceId, {
|
|
173
198
|
pairingToken,
|
|
174
199
|
pairedAt,
|
|
@@ -268,11 +293,13 @@ export class TomorrowOS extends EventEmitter {
|
|
|
268
293
|
return !!ws && ws.readyState === WebSocket.OPEN;
|
|
269
294
|
}
|
|
270
295
|
captureHelloMeta(deviceId, msg) {
|
|
296
|
+
const bootUptimeSec = parseBootUptimeSec(msg);
|
|
271
297
|
this.pendingDeviceMeta.set(deviceId, {
|
|
272
298
|
platform: typeof msg.platform === "string" ? msg.platform : undefined,
|
|
273
299
|
deviceName: typeof msg.deviceName === "string" ? msg.deviceName : undefined,
|
|
274
300
|
system: typeof msg.system === "string" ? msg.system : undefined,
|
|
275
301
|
bootedAt: typeof msg.bootedAt === "string" ? msg.bootedAt : undefined,
|
|
302
|
+
bootUptimeSec: bootUptimeSec ?? undefined,
|
|
276
303
|
playerVersion: typeof msg.playerVersion === "string" ? msg.playerVersion : undefined,
|
|
277
304
|
serialNumber: typeof msg.serialNumber === "string" ? msg.serialNumber : deviceId
|
|
278
305
|
});
|
|
@@ -313,11 +340,10 @@ export class TomorrowOS extends EventEmitter {
|
|
|
313
340
|
async touchPairedOnline(deviceId, msg) {
|
|
314
341
|
this.captureHelloMeta(deviceId, msg);
|
|
315
342
|
const now = new Date().toISOString();
|
|
316
|
-
const bootedAt = typeof msg.bootedAt === "string" ? msg.bootedAt : undefined;
|
|
317
343
|
const existing = await this.store.getPairedDevice(deviceId);
|
|
318
344
|
if (!existing)
|
|
319
345
|
return;
|
|
320
|
-
const lastBootAt =
|
|
346
|
+
const lastBootAt = lastBootAtFromHandshake(msg, existing.lastBootAt);
|
|
321
347
|
await this.store.setPairedDevice(deviceId, {
|
|
322
348
|
...existing,
|
|
323
349
|
deviceName: (typeof msg.deviceName === "string" ? msg.deviceName : undefined) ??
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
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
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "My Venue",
|
|
3
|
-
"tagline": "Digital signage",
|
|
4
|
-
"targetPlatforms": ["tizen"],
|
|
5
|
-
"primaryColor": "#FF8A3D",
|
|
6
|
-
"secondaryColor": "#F5F3EF",
|
|
7
|
-
"backgroundColor": "#FAFAF9",
|
|
8
|
-
"textColor": "#0A0908",
|
|
9
|
-
"logoPath": "./assets/logo.svg",
|
|
10
|
-
"fontFamily": "Inter",
|
|
11
|
-
"cms": {
|
|
12
|
-
"useCase": "other",
|
|
13
|
-
"hostingTarget": "self-hosted",
|
|
14
|
-
"expectedScreens": 5,
|
|
15
|
-
"features": {
|
|
16
|
-
"bulkCommands": false,
|
|
17
|
-
"proofOfPlay": false,
|
|
18
|
-
"contentScheduling": true,
|
|
19
|
-
"userManagement": false
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"protocolVersion": "1.0"
|
|
23
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "My Venue",
|
|
3
|
+
"tagline": "Digital signage",
|
|
4
|
+
"targetPlatforms": ["tizen"],
|
|
5
|
+
"primaryColor": "#FF8A3D",
|
|
6
|
+
"secondaryColor": "#F5F3EF",
|
|
7
|
+
"backgroundColor": "#FAFAF9",
|
|
8
|
+
"textColor": "#0A0908",
|
|
9
|
+
"logoPath": "./assets/logo.svg",
|
|
10
|
+
"fontFamily": "Inter",
|
|
11
|
+
"cms": {
|
|
12
|
+
"useCase": "other",
|
|
13
|
+
"hostingTarget": "self-hosted",
|
|
14
|
+
"expectedScreens": 5,
|
|
15
|
+
"features": {
|
|
16
|
+
"bulkCommands": false,
|
|
17
|
+
"proofOfPlay": false,
|
|
18
|
+
"contentScheduling": true,
|
|
19
|
+
"userManagement": false
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"protocolVersion": "1.0"
|
|
23
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
|
|
5
5
|
"private": true,
|
|
6
6
|
"type": "module",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"build-player": "tomorrowos build --platform tizen"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@tomorrowos/sdk": "^0.3.
|
|
13
|
+
"@tomorrowos/sdk": "^0.3.9"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^20.0.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
</div>
|
|
56
56
|
</section>
|
|
57
57
|
|
|
58
|
-
<section class="card" id="playlistEditorSection">
|
|
58
|
+
<section class="card hidden" id="playlistEditorSection">
|
|
59
59
|
<h2 id="editorTitle">Playlist editor</h2>
|
|
60
60
|
<p class="hint">Save confirms changes. Names must be unique. Schedule applies to this playlist only.</p>
|
|
61
61
|
<label class="field-label">
|
|
@@ -225,7 +225,7 @@ async function fetchPlaylists() {
|
|
|
225
225
|
showResult({
|
|
226
226
|
status: "failed",
|
|
227
227
|
error:
|
|
228
|
-
"CMS server is missing /playlists. Restart CMS with @tomorrowos/sdk 0.3.
|
|
228
|
+
"CMS server is missing /playlists. Restart CMS with @tomorrowos/sdk 0.3.9 or newer."
|
|
229
229
|
});
|
|
230
230
|
}
|
|
231
231
|
return;
|
|
@@ -234,7 +234,8 @@ async function fetchPlaylists() {
|
|
|
234
234
|
playlistsCatalog = data.playlists;
|
|
235
235
|
renderPlaylistCatalog();
|
|
236
236
|
if (selectedPlaylistId && !getSelectedPlaylist()) {
|
|
237
|
-
selectedPlaylistId =
|
|
237
|
+
selectedPlaylistId = null;
|
|
238
|
+
playlistDraftActive = false;
|
|
238
239
|
loadEditorFromSelection();
|
|
239
240
|
}
|
|
240
241
|
}
|
|
@@ -261,7 +262,7 @@ function renderPlaylistCatalog() {
|
|
|
261
262
|
const li = document.createElement("li");
|
|
262
263
|
li.className = "playlist-catalog-item";
|
|
263
264
|
if (pl.id === selectedPlaylistId) li.classList.add("playlist-catalog-item--active");
|
|
264
|
-
li.innerHTML = `<strong>${escapeHtml(pl.name)}</strong><small
|
|
265
|
+
li.innerHTML = `<strong>${escapeHtml(pl.name)}</strong><small>${(pl.items || []).length} items</small>`;
|
|
265
266
|
li.addEventListener("click", () => {
|
|
266
267
|
playlistDraftActive = false;
|
|
267
268
|
selectedPlaylistId = pl.id;
|
|
@@ -276,6 +277,12 @@ function isPlaylistEditorOpen() {
|
|
|
276
277
|
return playlistDraftActive || !!selectedPlaylistId;
|
|
277
278
|
}
|
|
278
279
|
|
|
280
|
+
function updatePlaylistEditorVisibility() {
|
|
281
|
+
const section = document.getElementById("playlistEditorSection");
|
|
282
|
+
if (!section) return;
|
|
283
|
+
section.classList.toggle("hidden", !isPlaylistEditorOpen());
|
|
284
|
+
}
|
|
285
|
+
|
|
279
286
|
function loadEditorFromSelection() {
|
|
280
287
|
const pl = getSelectedPlaylist();
|
|
281
288
|
const nameInput = document.getElementById("playlistName");
|
|
@@ -284,6 +291,7 @@ function loadEditorFromSelection() {
|
|
|
284
291
|
if (!pl) {
|
|
285
292
|
if (playlistDraftActive) {
|
|
286
293
|
if (editorTitle) editorTitle.textContent = "New playlist";
|
|
294
|
+
updatePlaylistEditorVisibility();
|
|
287
295
|
renderEditorAssets();
|
|
288
296
|
return;
|
|
289
297
|
}
|
|
@@ -292,6 +300,7 @@ function loadEditorFromSelection() {
|
|
|
292
300
|
if (nameInput) nameInput.value = "";
|
|
293
301
|
editorItems = [];
|
|
294
302
|
loadScheduleIntoForm(null);
|
|
303
|
+
updatePlaylistEditorVisibility();
|
|
295
304
|
renderEditorAssets();
|
|
296
305
|
return;
|
|
297
306
|
}
|
|
@@ -308,6 +317,7 @@ function loadEditorFromSelection() {
|
|
|
308
317
|
durationMs: normalizeDurationMs(item)
|
|
309
318
|
}));
|
|
310
319
|
renderEditorAssets();
|
|
320
|
+
updatePlaylistEditorVisibility();
|
|
311
321
|
}
|
|
312
322
|
|
|
313
323
|
function renderEditorAssets() {
|
|
@@ -478,6 +488,7 @@ function newPlaylistDraft() {
|
|
|
478
488
|
editorItems = [];
|
|
479
489
|
renderPlaylistCatalog();
|
|
480
490
|
renderEditorAssets();
|
|
491
|
+
updatePlaylistEditorVisibility();
|
|
481
492
|
const editorTitle = document.getElementById("editorTitle");
|
|
482
493
|
if (editorTitle) editorTitle.textContent = "New playlist";
|
|
483
494
|
document.getElementById("playlistEditorSection")?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
@@ -538,7 +549,7 @@ function renderDeviceCards() {
|
|
|
538
549
|
for (const p of pubs) {
|
|
539
550
|
const li = document.createElement("li");
|
|
540
551
|
const label = document.createElement("span");
|
|
541
|
-
label.textContent =
|
|
552
|
+
label.textContent = p.name;
|
|
542
553
|
const rm = document.createElement("button");
|
|
543
554
|
rm.type = "button";
|
|
544
555
|
rm.textContent = "Remove";
|
|
@@ -653,7 +664,7 @@ function openPublishModal(deviceId) {
|
|
|
653
664
|
cb.value = pl.id;
|
|
654
665
|
cb.dataset.name = pl.name;
|
|
655
666
|
label.appendChild(cb);
|
|
656
|
-
label.appendChild(document.createTextNode(` ${pl.name}
|
|
667
|
+
label.appendChild(document.createTextNode(` ${pl.name}`));
|
|
657
668
|
checklist.appendChild(label);
|
|
658
669
|
}
|
|
659
670
|
|
|
@@ -830,6 +841,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
830
841
|
const cmsBaseInput = document.getElementById("cmsDeviceBaseUrl");
|
|
831
842
|
if (savedMediaBase && cmsBaseInput) cmsBaseInput.value = savedMediaBase;
|
|
832
843
|
|
|
844
|
+
updatePlaylistEditorVisibility();
|
|
833
845
|
void fetchPlaylists();
|
|
834
846
|
startDevicePolling();
|
|
835
847
|
|