@tomorrowos/sdk 0.2.0 → 0.2.2
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
CHANGED
|
@@ -40,6 +40,8 @@ export interface DeviceListItem {
|
|
|
40
40
|
lastPolicyPushAt: string | null;
|
|
41
41
|
screenOnlineActive: boolean;
|
|
42
42
|
screenOnlineLabel: string;
|
|
43
|
+
/** ISO time when the TV WebSocket session last became active (not CMS browser). */
|
|
44
|
+
screenOnlineSince: string | null;
|
|
43
45
|
}
|
|
44
46
|
export declare class TomorrowOS extends EventEmitter {
|
|
45
47
|
readonly brand: TomorrowOSBrand;
|
|
@@ -76,6 +78,8 @@ export declare class TomorrowOS extends EventEmitter {
|
|
|
76
78
|
private mergePairedRecord;
|
|
77
79
|
private touchPairedOnline;
|
|
78
80
|
private touchPairedOffline;
|
|
81
|
+
/** Mark device offline immediately (e.g. reboot) before WebSocket close propagates. */
|
|
82
|
+
private forceDeviceOffline;
|
|
79
83
|
private recordPolicyPush;
|
|
80
84
|
private refreshPairedDeviceInfo;
|
|
81
85
|
device(deviceId: string): {
|
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;
|
|
1
|
+
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.js
CHANGED
|
@@ -16,12 +16,11 @@ function formatDurationMs(ms) {
|
|
|
16
16
|
const parts = [];
|
|
17
17
|
if (days > 0)
|
|
18
18
|
parts.push(`${days}d`);
|
|
19
|
-
if (hours > 0)
|
|
19
|
+
if (days > 0 || hours > 0)
|
|
20
20
|
parts.push(`${hours}h`);
|
|
21
|
-
if (minutes > 0)
|
|
21
|
+
if (days > 0 || hours > 0 || minutes > 0)
|
|
22
22
|
parts.push(`${minutes}m`);
|
|
23
|
-
|
|
24
|
-
parts.push(`${seconds}s`);
|
|
23
|
+
parts.push(`${seconds}s`);
|
|
25
24
|
return parts.join(" ");
|
|
26
25
|
}
|
|
27
26
|
function createSixDigitCode() {
|
|
@@ -161,6 +160,7 @@ export class TomorrowOS extends EventEmitter {
|
|
|
161
160
|
screenOnlineLabel = formatDurationMs(now - since);
|
|
162
161
|
}
|
|
163
162
|
}
|
|
163
|
+
const screenOnlineSince = connected && record.lastOnlineAt ? record.lastOnlineAt : null;
|
|
164
164
|
return {
|
|
165
165
|
deviceId,
|
|
166
166
|
connected,
|
|
@@ -173,7 +173,8 @@ export class TomorrowOS extends EventEmitter {
|
|
|
173
173
|
lastOfflineAt: record.lastOfflineAt ?? null,
|
|
174
174
|
lastPolicyPushAt: record.lastPolicyPushAt ?? null,
|
|
175
175
|
screenOnlineActive,
|
|
176
|
-
screenOnlineLabel
|
|
176
|
+
screenOnlineLabel,
|
|
177
|
+
screenOnlineSince
|
|
177
178
|
};
|
|
178
179
|
});
|
|
179
180
|
}
|
|
@@ -222,7 +223,28 @@ export class TomorrowOS extends EventEmitter {
|
|
|
222
223
|
return;
|
|
223
224
|
await this.store.setPairedDevice(deviceId, {
|
|
224
225
|
...existing,
|
|
225
|
-
lastOfflineAt: new Date().toISOString()
|
|
226
|
+
lastOfflineAt: new Date().toISOString(),
|
|
227
|
+
lastOnlineAt: undefined
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
/** Mark device offline immediately (e.g. reboot) before WebSocket close propagates. */
|
|
231
|
+
forceDeviceOffline(deviceId) {
|
|
232
|
+
const ws = this.devices.get(deviceId);
|
|
233
|
+
if (ws) {
|
|
234
|
+
try {
|
|
235
|
+
ws.close();
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
/* ignore */
|
|
239
|
+
}
|
|
240
|
+
if (this.devices.get(deviceId) === ws) {
|
|
241
|
+
this.devices.delete(deviceId);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
void this.touchPairedOffline(deviceId);
|
|
245
|
+
this.emit("device.offline", {
|
|
246
|
+
deviceId,
|
|
247
|
+
lastSeen: new Date().toISOString()
|
|
226
248
|
});
|
|
227
249
|
}
|
|
228
250
|
async recordPolicyPush(deviceId) {
|
|
@@ -540,6 +562,10 @@ export class TomorrowOS extends EventEmitter {
|
|
|
540
562
|
if (action === "content/set-policy" && result.status === "success") {
|
|
541
563
|
await this.recordPolicyPush(deviceId);
|
|
542
564
|
}
|
|
565
|
+
if (action === "reboot" &&
|
|
566
|
+
(result.status === "success" || result.status === "accepted")) {
|
|
567
|
+
this.forceDeviceOffline(deviceId);
|
|
568
|
+
}
|
|
543
569
|
sendJson(res, 200, { status: result.status, data: result.data });
|
|
544
570
|
}
|
|
545
571
|
catch (e) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.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
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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.2.
|
|
13
|
+
"@tomorrowos/sdk": "^0.2.2"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^20.0.0",
|
|
@@ -28,9 +28,9 @@ function formatDurationMs(ms) {
|
|
|
28
28
|
const seconds = totalSec % 60;
|
|
29
29
|
const parts = [];
|
|
30
30
|
if (days > 0) parts.push(`${days}d`);
|
|
31
|
-
if (hours > 0) parts.push(`${hours}h`);
|
|
32
|
-
if (minutes > 0) parts.push(`${minutes}m`);
|
|
33
|
-
|
|
31
|
+
if (days > 0 || hours > 0) parts.push(`${hours}h`);
|
|
32
|
+
if (days > 0 || hours > 0 || minutes > 0) parts.push(`${minutes}m`);
|
|
33
|
+
parts.push(`${seconds}s`);
|
|
34
34
|
return parts.join(" ");
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -48,9 +48,12 @@ function formatDateTimeSeconds(iso) {
|
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
/** TV online duration since last WebSocket active (device.lastOnlineAt), not this browser tab. */
|
|
52
|
+
function formatDeviceOnlineLabel(device) {
|
|
53
|
+
if (!device.connected) return "Not active";
|
|
54
|
+
const sinceIso = device.screenOnlineSince || device.lastOnlineAt;
|
|
55
|
+
if (!sinceIso) return "Not active";
|
|
56
|
+
const since = new Date(sinceIso).getTime();
|
|
54
57
|
if (Number.isNaN(since)) return "Not active";
|
|
55
58
|
return formatDurationMs(Date.now() - since);
|
|
56
59
|
}
|
|
@@ -61,7 +64,7 @@ function updateDeviceUptimeLabels() {
|
|
|
61
64
|
const device = devicesCache.find((d) => d.deviceId === deviceId);
|
|
62
65
|
if (!device) return;
|
|
63
66
|
const el = card.querySelector(".device-online-time");
|
|
64
|
-
if (el) el.textContent =
|
|
67
|
+
if (el) el.textContent = formatDeviceOnlineLabel(device);
|
|
65
68
|
});
|
|
66
69
|
}
|
|
67
70
|
|
|
@@ -117,7 +120,10 @@ function renderDeviceCards() {
|
|
|
117
120
|
const rows = [
|
|
118
121
|
["Device ID", device.deviceId],
|
|
119
122
|
["System", device.system || device.platform || "—"],
|
|
120
|
-
[
|
|
123
|
+
[
|
|
124
|
+
"Device online",
|
|
125
|
+
formatDeviceOnlineLabel(device)
|
|
126
|
+
],
|
|
121
127
|
["Last boot", formatDateTimeSeconds(device.lastBootAt)],
|
|
122
128
|
["Latest content push", formatDateTimeSeconds(device.lastPolicyPushAt)]
|
|
123
129
|
];
|
|
@@ -128,8 +134,10 @@ function renderDeviceCards() {
|
|
|
128
134
|
const dt = document.createElement("dt");
|
|
129
135
|
dt.textContent = label;
|
|
130
136
|
const dd = document.createElement("dd");
|
|
131
|
-
if (label === "
|
|
137
|
+
if (label === "Device online") {
|
|
132
138
|
dd.className = "device-online-time";
|
|
139
|
+
dd.title =
|
|
140
|
+
"Time since this TV's WebSocket session became active (updates every minute)";
|
|
133
141
|
}
|
|
134
142
|
dd.textContent = value;
|
|
135
143
|
row.appendChild(dt);
|
|
@@ -151,6 +159,13 @@ function renderDeviceCards() {
|
|
|
151
159
|
infoBtn.textContent = "Info";
|
|
152
160
|
infoBtn.addEventListener("click", () => deviceAction(device.deviceId, "get-info"));
|
|
153
161
|
|
|
162
|
+
const capsBtn = document.createElement("button");
|
|
163
|
+
capsBtn.type = "button";
|
|
164
|
+
capsBtn.textContent = "Capabilities";
|
|
165
|
+
capsBtn.addEventListener("click", () =>
|
|
166
|
+
deviceAction(device.deviceId, "get-capabilities")
|
|
167
|
+
);
|
|
168
|
+
|
|
154
169
|
const rebootBtn = document.createElement("button");
|
|
155
170
|
rebootBtn.type = "button";
|
|
156
171
|
rebootBtn.textContent = "Reboot";
|
|
@@ -169,6 +184,7 @@ function renderDeviceCards() {
|
|
|
169
184
|
|
|
170
185
|
actions.appendChild(publishBtn);
|
|
171
186
|
actions.appendChild(infoBtn);
|
|
187
|
+
actions.appendChild(capsBtn);
|
|
172
188
|
actions.appendChild(rebootBtn);
|
|
173
189
|
actions.appendChild(clearBtn);
|
|
174
190
|
actions.appendChild(unpairBtn);
|
|
@@ -585,7 +601,12 @@ async function deviceAction(deviceId, action) {
|
|
|
585
601
|
const res = await fetch(`/device/${encodeURIComponent(deviceId)}/${action}`, {
|
|
586
602
|
method: "POST"
|
|
587
603
|
});
|
|
588
|
-
|
|
604
|
+
const data = await res.json();
|
|
605
|
+
showResult({ deviceId, action, ...data });
|
|
606
|
+
|
|
607
|
+
if (action === "reboot" && res.ok) {
|
|
608
|
+
await fetchDevices();
|
|
609
|
+
}
|
|
589
610
|
}
|
|
590
611
|
|
|
591
612
|
document.addEventListener("DOMContentLoaded", () => {
|