@tomorrowos/sdk 0.3.6 → 0.3.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,52 @@ function formatDurationMs(ms) {
26
26
  parts.push(`${seconds}s`);
27
27
  return parts.join(" ");
28
28
  }
29
+ function parseBootIsoMs(iso) {
30
+ if (!iso)
31
+ return null;
32
+ const ms = new Date(iso).getTime();
33
+ return Number.isFinite(ms) ? ms : null;
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
+ }
58
+ /** Merge device-reported boot time with stored value (ignore page-reload noise, accept reboots). */
59
+ function resolveLastBootAt(incoming, existing) {
60
+ const inMs = parseBootIsoMs(incoming);
61
+ const exMs = parseBootIsoMs(existing);
62
+ if (inMs == null && exMs == null)
63
+ return undefined;
64
+ if (inMs == null)
65
+ return existing;
66
+ if (exMs == null)
67
+ return incoming;
68
+ const gapMs = inMs - exMs;
69
+ if (gapMs < 0)
70
+ return incoming;
71
+ if (gapMs > 120_000)
72
+ return incoming;
73
+ return new Date(Math.min(inMs, exMs)).toISOString();
74
+ }
29
75
  function resolveHelloDeviceId(msg) {
30
76
  const serial = typeof msg.serialNumber === "string" && msg.serialNumber.trim()
31
77
  ? msg.serialNumber.trim()
@@ -145,13 +191,16 @@ export class TomorrowOS extends EventEmitter {
145
191
  const pairedAt = new Date().toISOString();
146
192
  const meta = this.pendingDeviceMeta.get(deviceId);
147
193
  const now = pairedAt;
194
+ const lastBootAt = meta?.bootUptimeSec != null
195
+ ? resolveLastBootAt(bootAtFromUptimeSec(meta.bootUptimeSec), undefined)
196
+ : resolveLastBootAt(meta?.bootedAt, undefined);
148
197
  await this.store.setPairedDevice(deviceId, {
149
198
  pairingToken,
150
199
  pairedAt,
151
200
  deviceName: meta?.deviceName,
152
201
  platform: meta?.platform,
153
202
  system: meta?.system,
154
- lastBootAt: meta?.bootedAt ?? now,
203
+ ...(lastBootAt ? { lastBootAt } : {}),
155
204
  lastOnlineAt: this.isDeviceConnected(deviceId) ? now : undefined,
156
205
  lastOfflineAt: this.isDeviceConnected(deviceId) ? undefined : now
157
206
  });
@@ -244,11 +293,13 @@ export class TomorrowOS extends EventEmitter {
244
293
  return !!ws && ws.readyState === WebSocket.OPEN;
245
294
  }
246
295
  captureHelloMeta(deviceId, msg) {
296
+ const bootUptimeSec = parseBootUptimeSec(msg);
247
297
  this.pendingDeviceMeta.set(deviceId, {
248
298
  platform: typeof msg.platform === "string" ? msg.platform : undefined,
249
299
  deviceName: typeof msg.deviceName === "string" ? msg.deviceName : undefined,
250
300
  system: typeof msg.system === "string" ? msg.system : undefined,
251
301
  bootedAt: typeof msg.bootedAt === "string" ? msg.bootedAt : undefined,
302
+ bootUptimeSec: bootUptimeSec ?? undefined,
252
303
  playerVersion: typeof msg.playerVersion === "string" ? msg.playerVersion : undefined,
253
304
  serialNumber: typeof msg.serialNumber === "string" ? msg.serialNumber : deviceId
254
305
  });
@@ -289,10 +340,10 @@ export class TomorrowOS extends EventEmitter {
289
340
  async touchPairedOnline(deviceId, msg) {
290
341
  this.captureHelloMeta(deviceId, msg);
291
342
  const now = new Date().toISOString();
292
- const bootedAt = typeof msg.bootedAt === "string" ? msg.bootedAt : undefined;
293
343
  const existing = await this.store.getPairedDevice(deviceId);
294
344
  if (!existing)
295
345
  return;
346
+ const lastBootAt = lastBootAtFromHandshake(msg, existing.lastBootAt);
296
347
  await this.store.setPairedDevice(deviceId, {
297
348
  ...existing,
298
349
  deviceName: (typeof msg.deviceName === "string" ? msg.deviceName : undefined) ??
@@ -301,7 +352,7 @@ export class TomorrowOS extends EventEmitter {
301
352
  existing.platform,
302
353
  system: (typeof msg.system === "string" ? msg.system : undefined) ??
303
354
  existing.system,
304
- lastBootAt: bootedAt ?? existing.lastBootAt ?? now,
355
+ ...(lastBootAt ? { lastBootAt } : {}),
305
356
  lastOnlineAt: now,
306
357
  lastOfflineAt: existing.lastOfflineAt
307
358
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomorrowos/sdk",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
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.6"
13
+ "@tomorrowos/sdk": "^0.3.8"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/node": "^20.0.0",
@@ -47,7 +47,15 @@ function formatDateTimeSeconds(iso) {
47
47
  if (!iso) return "—";
48
48
  const d = new Date(iso);
49
49
  if (Number.isNaN(d.getTime())) return "—";
50
- return d.toLocaleString();
50
+ return d.toLocaleString(undefined, {
51
+ year: "numeric",
52
+ month: "short",
53
+ day: "numeric",
54
+ hour: "2-digit",
55
+ minute: "2-digit",
56
+ second: "2-digit",
57
+ hour12: false
58
+ });
51
59
  }
52
60
 
53
61
  function formatDeviceOnlineLabel(device) {
@@ -217,7 +225,7 @@ async function fetchPlaylists() {
217
225
  showResult({
218
226
  status: "failed",
219
227
  error:
220
- "CMS server is missing /playlists. Restart CMS with @tomorrowos/sdk 0.3.6 or newer."
228
+ "CMS server is missing /playlists. Restart CMS with @tomorrowos/sdk 0.3.8 or newer."
221
229
  });
222
230
  }
223
231
  return;