@tomorrowos/sdk 0.9.10 → 0.9.13
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/REPLIT_SETUP.md +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/sdk-version.d.ts +3 -0
- package/dist/sdk-version.d.ts.map +1 -0
- package/dist/sdk-version.js +15 -0
- package/dist/server-status.d.ts +2 -0
- package/dist/server-status.d.ts.map +1 -1
- package/dist/server-status.js +2 -0
- package/dist/store/postgres-store.d.ts.map +1 -1
- package/dist/store/postgres-store.js +13 -5
- package/dist/store/sqlite-store.d.ts.map +1 -1
- package/dist/store/sqlite-store.js +10 -5
- package/dist/store/types.d.ts +10 -0
- package/dist/store/types.d.ts.map +1 -1
- package/dist/tomorrowos.d.ts +13 -1
- package/dist/tomorrowos.d.ts.map +1 -1
- package/dist/tomorrowos.js +76 -1
- package/package.json +1 -1
- package/templates/cms-starter/package.json +2 -2
- package/templates/cms-starter/public/index.html +49 -0
- package/templates/cms-starter/public/methods.js +173 -1
- package/templates/cms-starter/public/panel.css +58 -1
package/REPLIT_SETUP.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export { TomorrowOS } from "./tomorrowos.js";
|
|
|
2
2
|
export type { DeviceListItem, DeviceScreenshotInfo, ListenOptions, TomorrowOSBrand, TomorrowOSOptions } from "./tomorrowos.js";
|
|
3
3
|
export type { ConnectorState, ConnectorStatus, ServerStatusReport, StatusBlocker } from "./server-status.js";
|
|
4
4
|
export { buildServerStatus } from "./server-status.js";
|
|
5
|
-
export
|
|
5
|
+
export { getSdkPackageVersion } from "./sdk-version.js";
|
|
6
|
+
export type { DeviceOnOffTimer, DevicePlaylistAssignment, DeviceRegistryRecord, PairedDeviceRecord, PendingCodeEntry, PendingCodeRecord, DeviceRegistryEntry, PlaylistItemRecord, PlaylistSchedule, PublishedPlaylistSnapshot, StoredPlaylist, TomorrowOSDataSnapshot, TomorrowOSMigratableStore, TomorrowOSStore } from "./store/types.js";
|
|
6
7
|
export { PlaylistCatalog } from "./playlist-catalog.js";
|
|
7
8
|
export type { BuiltDevicePolicy, SavePlaylistInput } from "./playlist-catalog.js";
|
|
8
9
|
export { DEFAULT_PAIRING_CODE_SECRET, generateDeterministicPairingCode, generateRandomPairingCode, isValidPairingCodeFormat, normalizePairingCode, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH, resolvePairingCodeSecret } from "./pairing-code.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,YAAY,EACV,gBAAgB,EAChB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EACL,2BAA2B,EAC3B,gCAAgC,EAChC,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { TomorrowOS } from "./tomorrowos.js";
|
|
2
2
|
export { buildServerStatus } from "./server-status.js";
|
|
3
|
+
export { getSdkPackageVersion } from "./sdk-version.js";
|
|
3
4
|
export { PlaylistCatalog } from "./playlist-catalog.js";
|
|
4
5
|
export { DEFAULT_PAIRING_CODE_SECRET, generateDeterministicPairingCode, generateRandomPairingCode, isValidPairingCodeFormat, normalizePairingCode, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH, resolvePairingCodeSecret } from "./pairing-code.js";
|
|
5
6
|
export { MemoryStore } from "./store/memory-store.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-version.d.ts","sourceRoot":"","sources":["../src/sdk-version.ts"],"names":[],"mappings":"AAIA,sFAAsF;AACtF,wBAAgB,oBAAoB,IAAI,MAAM,CAW7C"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
/** Installed `@tomorrowos/sdk` package version (from this package's package.json). */
|
|
5
|
+
export function getSdkPackageVersion() {
|
|
6
|
+
try {
|
|
7
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const pkgPath = path.join(here, "..", "package.json");
|
|
9
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
10
|
+
return String(pkg.version || "").trim() || "unknown";
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return "unknown";
|
|
14
|
+
}
|
|
15
|
+
}
|
package/dist/server-status.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface ServerStatusReport {
|
|
|
17
17
|
status: "success";
|
|
18
18
|
overall: "ok" | "degraded" | "blocked";
|
|
19
19
|
checkedAt: string;
|
|
20
|
+
/** Installed `@tomorrowos/sdk` version. */
|
|
21
|
+
sdkVersion: string;
|
|
20
22
|
connectors: ConnectorStatus[];
|
|
21
23
|
blockers: StatusBlocker[];
|
|
22
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-status.d.ts","sourceRoot":"","sources":["../src/server-status.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server-status.d.ts","sourceRoot":"","sources":["../src/server-status.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,IAAI,GAAG,UAAU,GAAG,SAAS,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,eAAe,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAoSD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CA6B7B"}
|
package/dist/server-status.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "fs/promises";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { pingCloudinary, resolveCloudinaryConfig } from "./cloudinary-storage.js";
|
|
4
|
+
import { getSdkPackageVersion } from "./sdk-version.js";
|
|
4
5
|
function isReplitHost(env) {
|
|
5
6
|
return !!(env.REPL_ID ||
|
|
6
7
|
env.REPLIT_DEV_DOMAIN ||
|
|
@@ -259,6 +260,7 @@ export async function buildServerStatus(options) {
|
|
|
259
260
|
status: "success",
|
|
260
261
|
overall,
|
|
261
262
|
checkedAt: new Date().toISOString(),
|
|
263
|
+
sdkVersion: getSdkPackageVersion(),
|
|
262
264
|
connectors,
|
|
263
265
|
blockers
|
|
264
266
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/store/postgres-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AAE3C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/store/postgres-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -125,6 +125,9 @@ export class PostgresStore {
|
|
|
125
125
|
ALTER TABLE paired_devices
|
|
126
126
|
ADD COLUMN IF NOT EXISTS last_screenshot_captured_at TEXT;
|
|
127
127
|
|
|
128
|
+
ALTER TABLE paired_devices
|
|
129
|
+
ADD COLUMN IF NOT EXISTS on_off_timer_json TEXT;
|
|
130
|
+
|
|
128
131
|
ALTER TABLE device_registry
|
|
129
132
|
ADD COLUMN IF NOT EXISTS display_name TEXT;
|
|
130
133
|
`);
|
|
@@ -363,9 +366,10 @@ export class PostgresStore {
|
|
|
363
366
|
last_offline_at,
|
|
364
367
|
last_policy_push_at,
|
|
365
368
|
last_screenshot_asset_id,
|
|
366
|
-
last_screenshot_captured_at
|
|
369
|
+
last_screenshot_captured_at,
|
|
370
|
+
on_off_timer_json
|
|
367
371
|
)
|
|
368
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
|
372
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
|
369
373
|
ON CONFLICT (device_id) DO UPDATE SET
|
|
370
374
|
pairing_token = EXCLUDED.pairing_token,
|
|
371
375
|
paired_at = EXCLUDED.paired_at,
|
|
@@ -379,7 +383,8 @@ export class PostgresStore {
|
|
|
379
383
|
last_offline_at = EXCLUDED.last_offline_at,
|
|
380
384
|
last_policy_push_at = EXCLUDED.last_policy_push_at,
|
|
381
385
|
last_screenshot_asset_id = EXCLUDED.last_screenshot_asset_id,
|
|
382
|
-
last_screenshot_captured_at = EXCLUDED.last_screenshot_captured_at
|
|
386
|
+
last_screenshot_captured_at = EXCLUDED.last_screenshot_captured_at,
|
|
387
|
+
on_off_timer_json = EXCLUDED.on_off_timer_json
|
|
383
388
|
`, [
|
|
384
389
|
deviceId,
|
|
385
390
|
record.pairingToken,
|
|
@@ -394,7 +399,8 @@ export class PostgresStore {
|
|
|
394
399
|
record.lastOfflineAt ?? null,
|
|
395
400
|
record.lastPolicyPushAt ?? null,
|
|
396
401
|
record.lastScreenshotAssetId ?? null,
|
|
397
|
-
record.lastScreenshotCapturedAt ?? null
|
|
402
|
+
record.lastScreenshotCapturedAt ?? null,
|
|
403
|
+
record.onOffTimer ? JSON.stringify(record.onOffTimer) : null
|
|
398
404
|
]);
|
|
399
405
|
}
|
|
400
406
|
async getPairedDevice(deviceId) {
|
|
@@ -626,6 +632,7 @@ export class PostgresStore {
|
|
|
626
632
|
};
|
|
627
633
|
}
|
|
628
634
|
mapPairedDeviceRow(row) {
|
|
635
|
+
const onOffTimer = parseJson(row.on_off_timer_json, null);
|
|
629
636
|
return {
|
|
630
637
|
pairingToken: row.pairing_token,
|
|
631
638
|
pairedAt: row.paired_at,
|
|
@@ -639,7 +646,8 @@ export class PostgresStore {
|
|
|
639
646
|
lastOfflineAt: optionalString(row.last_offline_at),
|
|
640
647
|
lastPolicyPushAt: optionalString(row.last_policy_push_at),
|
|
641
648
|
lastScreenshotAssetId: optionalString(row.last_screenshot_asset_id),
|
|
642
|
-
lastScreenshotCapturedAt: optionalString(row.last_screenshot_captured_at)
|
|
649
|
+
lastScreenshotCapturedAt: optionalString(row.last_screenshot_captured_at),
|
|
650
|
+
...(onOffTimer ? { onOffTimer } : {})
|
|
643
651
|
};
|
|
644
652
|
}
|
|
645
653
|
mapPlaylistRow(row) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/store/sqlite-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/store/sqlite-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -122,6 +122,7 @@ export class SQLiteStore {
|
|
|
122
122
|
this.addColumnIfMissing("paired_devices", "system_version TEXT");
|
|
123
123
|
this.addColumnIfMissing("paired_devices", "last_screenshot_asset_id TEXT");
|
|
124
124
|
this.addColumnIfMissing("paired_devices", "last_screenshot_captured_at TEXT");
|
|
125
|
+
this.addColumnIfMissing("paired_devices", "on_off_timer_json TEXT");
|
|
125
126
|
this.addColumnIfMissing("device_registry", "display_name TEXT");
|
|
126
127
|
this.migrateDropPlaylistVersionColumns();
|
|
127
128
|
this.migrateDropUploadedAssetStorageProvider();
|
|
@@ -413,9 +414,10 @@ export class SQLiteStore {
|
|
|
413
414
|
last_offline_at,
|
|
414
415
|
last_policy_push_at,
|
|
415
416
|
last_screenshot_asset_id,
|
|
416
|
-
last_screenshot_captured_at
|
|
417
|
+
last_screenshot_captured_at,
|
|
418
|
+
on_off_timer_json
|
|
417
419
|
)
|
|
418
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
420
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
419
421
|
ON CONFLICT(device_id) DO UPDATE SET
|
|
420
422
|
pairing_token = excluded.pairing_token,
|
|
421
423
|
paired_at = excluded.paired_at,
|
|
@@ -429,8 +431,9 @@ export class SQLiteStore {
|
|
|
429
431
|
last_offline_at = excluded.last_offline_at,
|
|
430
432
|
last_policy_push_at = excluded.last_policy_push_at,
|
|
431
433
|
last_screenshot_asset_id = excluded.last_screenshot_asset_id,
|
|
432
|
-
last_screenshot_captured_at = excluded.last_screenshot_captured_at
|
|
433
|
-
|
|
434
|
+
last_screenshot_captured_at = excluded.last_screenshot_captured_at,
|
|
435
|
+
on_off_timer_json = excluded.on_off_timer_json
|
|
436
|
+
`).run(deviceId, record.pairingToken, record.pairedAt, record.deviceName ?? null, record.platform ?? null, record.system ?? null, record.playerVersion ?? null, record.systemVersion ?? null, record.lastBootAt ?? null, record.lastOnlineAt ?? null, record.lastOfflineAt ?? null, record.lastPolicyPushAt ?? null, record.lastScreenshotAssetId ?? null, record.lastScreenshotCapturedAt ?? null, record.onOffTimer ? JSON.stringify(record.onOffTimer) : null);
|
|
434
437
|
}
|
|
435
438
|
async getPairedDevice(deviceId) {
|
|
436
439
|
const row = this.db.prepare(`
|
|
@@ -606,6 +609,7 @@ export class SQLiteStore {
|
|
|
606
609
|
};
|
|
607
610
|
}
|
|
608
611
|
mapPairedDeviceRow(row) {
|
|
612
|
+
const onOffTimer = parseJson(row.on_off_timer_json, null);
|
|
609
613
|
return {
|
|
610
614
|
pairingToken: row.pairing_token,
|
|
611
615
|
pairedAt: row.paired_at,
|
|
@@ -619,7 +623,8 @@ export class SQLiteStore {
|
|
|
619
623
|
lastOfflineAt: optionalString(row.last_offline_at),
|
|
620
624
|
lastPolicyPushAt: optionalString(row.last_policy_push_at),
|
|
621
625
|
lastScreenshotAssetId: optionalString(row.last_screenshot_asset_id),
|
|
622
|
-
lastScreenshotCapturedAt: optionalString(row.last_screenshot_captured_at)
|
|
626
|
+
lastScreenshotCapturedAt: optionalString(row.last_screenshot_captured_at),
|
|
627
|
+
...(onOffTimer ? { onOffTimer } : {})
|
|
623
628
|
};
|
|
624
629
|
}
|
|
625
630
|
mapPlaylistRow(row) {
|
package/dist/store/types.d.ts
CHANGED
|
@@ -16,6 +16,14 @@ export interface DeviceRegistryRecord {
|
|
|
16
16
|
/** CMS operator display name — survives unpair / re-pair. */
|
|
17
17
|
displayName?: string;
|
|
18
18
|
}
|
|
19
|
+
/** Device-local daily screen on/off window (HH:mm, device clock). Off = panel mute. */
|
|
20
|
+
export interface DeviceOnOffTimer {
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
/** HH:mm — unmute / screen on */
|
|
23
|
+
turnOnAt: string;
|
|
24
|
+
/** HH:mm — panel mute / screen off (device stays CMS-connected) */
|
|
25
|
+
turnOffAt: string;
|
|
26
|
+
}
|
|
19
27
|
export interface PairedDeviceRecord {
|
|
20
28
|
pairingToken: string;
|
|
21
29
|
pairedAt: string;
|
|
@@ -31,6 +39,8 @@ export interface PairedDeviceRecord {
|
|
|
31
39
|
/** Latest device screenshot stored via uploaded_assets (local or Cloudinary). */
|
|
32
40
|
lastScreenshotAssetId?: string;
|
|
33
41
|
lastScreenshotCapturedAt?: string;
|
|
42
|
+
/** Tizen screen mute schedule (panel mute; not power off). */
|
|
43
|
+
onOffTimer?: DeviceOnOffTimer;
|
|
34
44
|
}
|
|
35
45
|
export interface PairedDeviceEntry {
|
|
36
46
|
deviceId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wFAAwF;AACxF,MAAM,WAAW,oBAAoB;IACnC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,wBAAwB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
2
|
import http from "http";
|
|
3
3
|
import { PlaylistCatalog, type BuiltDevicePolicy } from "./playlist-catalog.js";
|
|
4
|
-
import type { TomorrowOSStore } from "./store/types.js";
|
|
4
|
+
import type { DeviceOnOffTimer, TomorrowOSStore } from "./store/types.js";
|
|
5
5
|
export type { ConnectorState, ConnectorStatus, ServerStatusReport, StatusBlocker } from "./server-status.js";
|
|
6
6
|
export interface TomorrowOSBrand {
|
|
7
7
|
name?: string;
|
|
@@ -66,6 +66,8 @@ export interface DeviceListItem {
|
|
|
66
66
|
url: string;
|
|
67
67
|
capturedAt: string;
|
|
68
68
|
} | null;
|
|
69
|
+
/** Tizen daily screen mute schedule (panel mute; device stays connected). */
|
|
70
|
+
onOffTimer: DeviceOnOffTimer | null;
|
|
69
71
|
}
|
|
70
72
|
export interface DeviceLogEntry {
|
|
71
73
|
timestamp: string;
|
|
@@ -123,6 +125,16 @@ export declare class TomorrowOS extends EventEmitter {
|
|
|
123
125
|
};
|
|
124
126
|
/** List paired devices with live connection + timing fields for the CMS panel. */
|
|
125
127
|
listDevices(): Promise<DeviceListItem[]>;
|
|
128
|
+
/**
|
|
129
|
+
* Persist a daily on/off timer (Tizen panel mute). Saved even if offline;
|
|
130
|
+
* pushed to the player when connected.
|
|
131
|
+
*/
|
|
132
|
+
setDeviceOnOffTimer(deviceId: string, timerInput: unknown): Promise<{
|
|
133
|
+
deviceId: string;
|
|
134
|
+
onOffTimer: DeviceOnOffTimer;
|
|
135
|
+
pushed: boolean;
|
|
136
|
+
}>;
|
|
137
|
+
private pushOnOffTimerToDevice;
|
|
126
138
|
setDeviceName(deviceId: string, deviceName: string): Promise<{
|
|
127
139
|
deviceId: string;
|
|
128
140
|
deviceName: 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;AAYxB,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.js
CHANGED
|
@@ -45,6 +45,23 @@ function normalizeDeviceSystemLabel(platform, system) {
|
|
|
45
45
|
const label = typeof system === "string" ? system.trim() : "";
|
|
46
46
|
return label || undefined;
|
|
47
47
|
}
|
|
48
|
+
const ON_OFF_TIME_RE = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
49
|
+
function normalizeOnOffTimer(input) {
|
|
50
|
+
if (!input || typeof input !== "object") {
|
|
51
|
+
throw new Error("onOffTimer object is required");
|
|
52
|
+
}
|
|
53
|
+
const body = input;
|
|
54
|
+
const enabled = Boolean(body.enabled);
|
|
55
|
+
const turnOnAt = String(body.turnOnAt ?? "").trim();
|
|
56
|
+
const turnOffAt = String(body.turnOffAt ?? "").trim();
|
|
57
|
+
if (!ON_OFF_TIME_RE.test(turnOnAt) || !ON_OFF_TIME_RE.test(turnOffAt)) {
|
|
58
|
+
throw new Error("turnOnAt and turnOffAt must be HH:mm (24h)");
|
|
59
|
+
}
|
|
60
|
+
if (turnOnAt === turnOffAt) {
|
|
61
|
+
throw new Error("turnOnAt and turnOffAt must be different");
|
|
62
|
+
}
|
|
63
|
+
return { enabled, turnOnAt, turnOffAt };
|
|
64
|
+
}
|
|
48
65
|
function isRemoteUploadedAssetUrl(url) {
|
|
49
66
|
return /^https?:\/\//i.test(String(url || "").trim());
|
|
50
67
|
}
|
|
@@ -284,6 +301,9 @@ export class TomorrowOS extends EventEmitter {
|
|
|
284
301
|
void this.pushLatestPolicyToDevice(deviceId).catch((err) => {
|
|
285
302
|
console.error("[TomorrowOS] pushLatestPolicy on paired failed:", err);
|
|
286
303
|
});
|
|
304
|
+
void this.pushOnOffTimerToDevice(deviceId).catch((err) => {
|
|
305
|
+
console.error("[TomorrowOS] pushOnOffTimer on paired failed:", err);
|
|
306
|
+
});
|
|
287
307
|
}
|
|
288
308
|
this.emit("device.paired", { deviceId });
|
|
289
309
|
return { deviceId };
|
|
@@ -384,10 +404,48 @@ export class TomorrowOS extends EventEmitter {
|
|
|
384
404
|
screenOnlineSince,
|
|
385
405
|
latestErrorAt: latestError?.timestamp ?? null,
|
|
386
406
|
latestErrorMessage: latestError?.message ?? null,
|
|
387
|
-
latestScreenshot
|
|
407
|
+
latestScreenshot,
|
|
408
|
+
onOffTimer: record.onOffTimer ?? null
|
|
388
409
|
};
|
|
389
410
|
}));
|
|
390
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Persist a daily on/off timer (Tizen panel mute). Saved even if offline;
|
|
414
|
+
* pushed to the player when connected.
|
|
415
|
+
*/
|
|
416
|
+
async setDeviceOnOffTimer(deviceId, timerInput) {
|
|
417
|
+
const id = String(deviceId || "").trim();
|
|
418
|
+
if (!id) {
|
|
419
|
+
throw new Error("deviceId is required");
|
|
420
|
+
}
|
|
421
|
+
const existing = await this.store.getPairedDevice(id);
|
|
422
|
+
if (!existing) {
|
|
423
|
+
throw new Error("Device is not paired");
|
|
424
|
+
}
|
|
425
|
+
const onOffTimer = normalizeOnOffTimer(timerInput);
|
|
426
|
+
await this.store.setPairedDevice(id, {
|
|
427
|
+
...existing,
|
|
428
|
+
onOffTimer
|
|
429
|
+
});
|
|
430
|
+
const pushed = await this.pushOnOffTimerToDevice(id);
|
|
431
|
+
return { deviceId: id, onOffTimer, pushed };
|
|
432
|
+
}
|
|
433
|
+
async pushOnOffTimerToDevice(deviceId) {
|
|
434
|
+
const existing = await this.store.getPairedDevice(deviceId);
|
|
435
|
+
if (!existing?.onOffTimer)
|
|
436
|
+
return false;
|
|
437
|
+
const ws = this.devices.get(deviceId);
|
|
438
|
+
if (!ws || ws.readyState !== WebSocket.OPEN)
|
|
439
|
+
return false;
|
|
440
|
+
try {
|
|
441
|
+
const result = await this.sendCommandToSocket(ws, deviceId, "device.display.setOnOffTimer", { onOffTimer: existing.onOffTimer }, 15_000);
|
|
442
|
+
return result.status === "success" || result.status === "accepted";
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
console.error("[TomorrowOS] pushOnOffTimer failed:", err);
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
391
449
|
async setDeviceName(deviceId, deviceName) {
|
|
392
450
|
const id = String(deviceId || "").trim();
|
|
393
451
|
if (!id) {
|
|
@@ -1091,6 +1149,20 @@ export class TomorrowOS extends EventEmitter {
|
|
|
1091
1149
|
}
|
|
1092
1150
|
return;
|
|
1093
1151
|
}
|
|
1152
|
+
const deviceOnOffTimer = /^\/device\/([^/]+)\/on-off-timer$/.exec(pathname);
|
|
1153
|
+
if (req.method === "POST" && deviceOnOffTimer) {
|
|
1154
|
+
const deviceId = decodeURIComponent(deviceOnOffTimer[1]);
|
|
1155
|
+
const body = (await readJsonBody(req));
|
|
1156
|
+
try {
|
|
1157
|
+
const result = await this.setDeviceOnOffTimer(deviceId, body.onOffTimer ?? body);
|
|
1158
|
+
sendJson(res, 200, { status: "success", ...result });
|
|
1159
|
+
}
|
|
1160
|
+
catch (e) {
|
|
1161
|
+
const msg = e instanceof Error ? e.message : "On/off timer failed";
|
|
1162
|
+
sendJson(res, 400, { status: "failed", error: msg });
|
|
1163
|
+
}
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1094
1166
|
if (req.method === "GET" && pathname === "/playlists") {
|
|
1095
1167
|
const playlists = await this.playlists.listPlaylists();
|
|
1096
1168
|
sendJson(res, 200, { status: "success", playlists });
|
|
@@ -1477,6 +1549,9 @@ export class TomorrowOS extends EventEmitter {
|
|
|
1477
1549
|
void this.pushLatestPolicyToDevice(deviceId).catch((err) => {
|
|
1478
1550
|
console.error("[TomorrowOS] pushLatestPolicy on resume failed:", err);
|
|
1479
1551
|
});
|
|
1552
|
+
void this.pushOnOffTimerToDevice(deviceId).catch((err) => {
|
|
1553
|
+
console.error("[TomorrowOS] pushOnOffTimer on resume failed:", err);
|
|
1554
|
+
});
|
|
1480
1555
|
})();
|
|
1481
1556
|
return;
|
|
1482
1557
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.13",
|
|
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.9.
|
|
3
|
+
"version": "0.9.13",
|
|
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",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"build-player": "tomorrowos build --platform tizen"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@tomorrowos/sdk": "^0.9.
|
|
16
|
+
"@tomorrowos/sdk": "^0.9.13",
|
|
17
17
|
"dotenv": "^17.2.3",
|
|
18
18
|
"tsx": "^4.19.0"
|
|
19
19
|
},
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
<button type="button" id="downloadPlayersBtn" class="download-players-trigger">
|
|
24
24
|
Download Players
|
|
25
25
|
</button>
|
|
26
|
+
<button type="button" id="updateSdkBtn" class="download-players-trigger">
|
|
27
|
+
Update SDK
|
|
28
|
+
</button>
|
|
26
29
|
</div>
|
|
27
30
|
</aside>
|
|
28
31
|
|
|
@@ -177,6 +180,35 @@
|
|
|
177
180
|
</div>
|
|
178
181
|
</div>
|
|
179
182
|
|
|
183
|
+
<div id="onOffTimerModal" class="modal hidden" role="dialog" aria-modal="true">
|
|
184
|
+
<div class="modal-backdrop" data-close-on-off-timer-modal="1"></div>
|
|
185
|
+
<div class="modal-card">
|
|
186
|
+
<h2>On / off timer</h2>
|
|
187
|
+
<p class="hint" id="onOffTimerModalHint">
|
|
188
|
+
Daily schedule on the device clock. Off uses Samsung panel mute (screen black); the player stays connected to the CMS.
|
|
189
|
+
</p>
|
|
190
|
+
<label class="field-label on-off-timer-enable">
|
|
191
|
+
<input type="checkbox" id="onOffTimerEnabled" />
|
|
192
|
+
Enable daily timer
|
|
193
|
+
</label>
|
|
194
|
+
<div class="on-off-timer-grid">
|
|
195
|
+
<label class="field-label">
|
|
196
|
+
Turn on (unmute)
|
|
197
|
+
<input type="time" id="onOffTimerTurnOnAt" value="06:00" />
|
|
198
|
+
</label>
|
|
199
|
+
<label class="field-label">
|
|
200
|
+
Turn off (mute)
|
|
201
|
+
<input type="time" id="onOffTimerTurnOffAt" value="18:00" />
|
|
202
|
+
</label>
|
|
203
|
+
</div>
|
|
204
|
+
<p class="hint" id="onOffTimerModalStatus"></p>
|
|
205
|
+
<div class="modal-actions">
|
|
206
|
+
<button type="button" id="onOffTimerSaveBtn" class="primary">Save</button>
|
|
207
|
+
<button type="button" data-close-on-off-timer-modal="1">Cancel</button>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
180
212
|
<div id="downloadFailedModal" class="modal hidden" role="dialog" aria-modal="true">
|
|
181
213
|
<div class="modal-backdrop" data-close-download-failed-modal="1"></div>
|
|
182
214
|
<div class="modal-card">
|
|
@@ -194,6 +226,23 @@
|
|
|
194
226
|
</div>
|
|
195
227
|
</div>
|
|
196
228
|
|
|
229
|
+
<div id="updateSdkModal" class="modal hidden" role="dialog" aria-modal="true" aria-labelledby="updateSdkModalTitle">
|
|
230
|
+
<div class="modal-backdrop" data-close-update-sdk-modal="1"></div>
|
|
231
|
+
<div class="modal-card update-sdk-modal-card">
|
|
232
|
+
<h2 id="updateSdkModalTitle">Update SDK</h2>
|
|
233
|
+
<p class="hint">Current installed SDK version:</p>
|
|
234
|
+
<p class="update-sdk-version" id="updateSdkVersionLabel">Loading…</p>
|
|
235
|
+
<p class="hint">
|
|
236
|
+
To upgrade on Replit (or another Agent platform), paste this prompt into the Agent chat:
|
|
237
|
+
</p>
|
|
238
|
+
<pre class="update-sdk-prompt" id="updateSdkPromptText">Follow @tomorrowos/sdk REPLIT_UPGRADE.md to upgrade my CMS with the latest SDK.</pre>
|
|
239
|
+
<div class="modal-actions update-sdk-modal-actions">
|
|
240
|
+
<button type="button" id="copyUpdateSdkPromptBtn" class="primary">Copy prompt</button>
|
|
241
|
+
<button type="button" data-close-update-sdk-modal="1">Close</button>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
197
246
|
<div id="downloadPlayersModal" class="modal hidden" role="dialog" aria-modal="true" aria-labelledby="downloadPlayersModalTitle">
|
|
198
247
|
<div class="modal-backdrop" data-close-download-players-modal="1"></div>
|
|
199
248
|
<div class="modal-card download-players-modal-card">
|
|
@@ -28,8 +28,13 @@ let editingDeviceNameValue = "";
|
|
|
28
28
|
|
|
29
29
|
let devicePollTimer = null;
|
|
30
30
|
let serverStatusTimer = null;
|
|
31
|
+
/** @type {string} Latest known `@tomorrowos/sdk` version from GET /status. */
|
|
32
|
+
let cachedSdkVersion = "";
|
|
31
33
|
/** @type {number|null} CMS server boot time (ms) from GET /devices. */
|
|
32
34
|
let serverStartedAtMs = null;
|
|
35
|
+
|
|
36
|
+
const UPDATE_SDK_PROMPT =
|
|
37
|
+
"Follow @tomorrowos/sdk REPLIT_UPGRADE.md to upgrade my CMS with the latest SDK.";
|
|
33
38
|
/** @type {ReturnType<typeof setTimeout>|null} */
|
|
34
39
|
let reconnectGraceTimer = null;
|
|
35
40
|
let uploadQueue = [];
|
|
@@ -1202,7 +1207,6 @@ function renderDeviceCards() {
|
|
|
1202
1207
|
const primaryRows = [
|
|
1203
1208
|
["Device ID", device.deviceId],
|
|
1204
1209
|
["System", device.system || device.platform || "—"],
|
|
1205
|
-
["System version", device.systemVersion || "—"],
|
|
1206
1210
|
["Player version", device.playerVersion || "—"]
|
|
1207
1211
|
];
|
|
1208
1212
|
for (const [label, value] of primaryRows) {
|
|
@@ -1274,6 +1278,12 @@ function renderDeviceCards() {
|
|
|
1274
1278
|
latestScreenshotBtn.textContent = "Last screen";
|
|
1275
1279
|
latestScreenshotBtn.addEventListener("click", () => viewLatestScreenshot(device.deviceId));
|
|
1276
1280
|
|
|
1281
|
+
const timerBtn = document.createElement("button");
|
|
1282
|
+
timerBtn.type = "button";
|
|
1283
|
+
timerBtn.textContent = device.onOffTimer?.enabled ? "Timer · On" : "Timer";
|
|
1284
|
+
timerBtn.title = "Daily screen on/off (Tizen panel mute)";
|
|
1285
|
+
timerBtn.addEventListener("click", () => openOnOffTimerModal(device.deviceId));
|
|
1286
|
+
|
|
1277
1287
|
const unpairBtn = document.createElement("button");
|
|
1278
1288
|
unpairBtn.type = "button";
|
|
1279
1289
|
unpairBtn.className = "danger";
|
|
@@ -1288,6 +1298,9 @@ function renderDeviceCards() {
|
|
|
1288
1298
|
actions.appendChild(logsBtn);
|
|
1289
1299
|
actions.appendChild(screenshotBtn);
|
|
1290
1300
|
actions.appendChild(latestScreenshotBtn);
|
|
1301
|
+
if (isTizenDevice(device)) {
|
|
1302
|
+
actions.appendChild(timerBtn);
|
|
1303
|
+
}
|
|
1291
1304
|
actions.appendChild(unpairBtn);
|
|
1292
1305
|
|
|
1293
1306
|
card.appendChild(header);
|
|
@@ -1780,6 +1793,106 @@ function closeScreenshotModal() {
|
|
|
1780
1793
|
modal?.classList.add("hidden");
|
|
1781
1794
|
}
|
|
1782
1795
|
|
|
1796
|
+
function isTizenDevice(device) {
|
|
1797
|
+
const platform = String(device?.platform || "").toLowerCase();
|
|
1798
|
+
const system = String(device?.system || "").toLowerCase();
|
|
1799
|
+
return platform.includes("tizen") || system.includes("tizen");
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
function normalizeTimeInputValue(value, fallback) {
|
|
1803
|
+
const raw = String(value || "").trim();
|
|
1804
|
+
const match = /^(\d{1,2}):(\d{2})/.exec(raw);
|
|
1805
|
+
if (!match) return fallback;
|
|
1806
|
+
const hh = Math.min(23, Math.max(0, Number(match[1])));
|
|
1807
|
+
const mm = Math.min(59, Math.max(0, Number(match[2])));
|
|
1808
|
+
if (Number.isNaN(hh) || Number.isNaN(mm)) return fallback;
|
|
1809
|
+
return `${String(hh).padStart(2, "0")}:${String(mm).padStart(2, "0")}`;
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
let onOffTimerModalDeviceId = null;
|
|
1813
|
+
|
|
1814
|
+
function openOnOffTimerModal(deviceId) {
|
|
1815
|
+
const device = devicesCache.find((d) => d.deviceId === deviceId);
|
|
1816
|
+
if (!device) return;
|
|
1817
|
+
if (!isTizenDevice(device)) {
|
|
1818
|
+
alert("On/off timer is available for Samsung Tizen players only.");
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
onOffTimerModalDeviceId = deviceId;
|
|
1823
|
+
const modal = document.getElementById("onOffTimerModal");
|
|
1824
|
+
const hint = document.getElementById("onOffTimerModalHint");
|
|
1825
|
+
const status = document.getElementById("onOffTimerModalStatus");
|
|
1826
|
+
const enabledEl = document.getElementById("onOffTimerEnabled");
|
|
1827
|
+
const onEl = document.getElementById("onOffTimerTurnOnAt");
|
|
1828
|
+
const offEl = document.getElementById("onOffTimerTurnOffAt");
|
|
1829
|
+
if (!modal || !enabledEl || !onEl || !offEl) return;
|
|
1830
|
+
|
|
1831
|
+
const timer = device.onOffTimer || {};
|
|
1832
|
+
enabledEl.checked = Boolean(timer.enabled);
|
|
1833
|
+
onEl.value = normalizeTimeInputValue(timer.turnOnAt, "06:00");
|
|
1834
|
+
offEl.value = normalizeTimeInputValue(timer.turnOffAt, "18:00");
|
|
1835
|
+
if (hint) {
|
|
1836
|
+
hint.textContent =
|
|
1837
|
+
"Daily schedule on the device clock. Off uses Samsung panel mute (screen black); the player stays connected to the CMS.";
|
|
1838
|
+
}
|
|
1839
|
+
if (status) {
|
|
1840
|
+
status.textContent = device.connected
|
|
1841
|
+
? "Device is online — save will apply immediately."
|
|
1842
|
+
: "Device is offline — schedule will save and apply when it reconnects.";
|
|
1843
|
+
}
|
|
1844
|
+
modal.classList.remove("hidden");
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
function closeOnOffTimerModal() {
|
|
1848
|
+
const modal = document.getElementById("onOffTimerModal");
|
|
1849
|
+
if (modal) modal.classList.add("hidden");
|
|
1850
|
+
onOffTimerModalDeviceId = null;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
async function saveOnOffTimerModal() {
|
|
1854
|
+
const deviceId = onOffTimerModalDeviceId;
|
|
1855
|
+
if (!deviceId) return;
|
|
1856
|
+
|
|
1857
|
+
const enabledEl = document.getElementById("onOffTimerEnabled");
|
|
1858
|
+
const onEl = document.getElementById("onOffTimerTurnOnAt");
|
|
1859
|
+
const offEl = document.getElementById("onOffTimerTurnOffAt");
|
|
1860
|
+
const saveBtn = document.getElementById("onOffTimerSaveBtn");
|
|
1861
|
+
if (!enabledEl || !onEl || !offEl) return;
|
|
1862
|
+
|
|
1863
|
+
const onOffTimer = {
|
|
1864
|
+
enabled: Boolean(enabledEl.checked),
|
|
1865
|
+
turnOnAt: normalizeTimeInputValue(onEl.value, "06:00"),
|
|
1866
|
+
turnOffAt: normalizeTimeInputValue(offEl.value, "18:00")
|
|
1867
|
+
};
|
|
1868
|
+
|
|
1869
|
+
if (onOffTimer.turnOnAt === onOffTimer.turnOffAt) {
|
|
1870
|
+
alert("Turn on and turn off times must be different.");
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
if (saveBtn) saveBtn.disabled = true;
|
|
1875
|
+
try {
|
|
1876
|
+
const res = await fetch(`/device/${encodeURIComponent(deviceId)}/on-off-timer`, {
|
|
1877
|
+
method: "POST",
|
|
1878
|
+
headers: { "Content-Type": "application/json" },
|
|
1879
|
+
body: JSON.stringify({ onOffTimer })
|
|
1880
|
+
});
|
|
1881
|
+
const data = await res.json();
|
|
1882
|
+
if (!res.ok || data.status === "failed") {
|
|
1883
|
+
alert(data.error || "Could not save on/off timer");
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
showResult({ deviceId, onOffTimer: data.onOffTimer, pushed: data.pushed });
|
|
1887
|
+
closeOnOffTimerModal();
|
|
1888
|
+
await fetchDevices();
|
|
1889
|
+
} catch (err) {
|
|
1890
|
+
alert(err?.message || "Could not save on/off timer");
|
|
1891
|
+
} finally {
|
|
1892
|
+
if (saveBtn) saveBtn.disabled = false;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1783
1896
|
function openDownloadFailedModal(detail) {
|
|
1784
1897
|
const modal = document.getElementById("downloadFailedModal");
|
|
1785
1898
|
const message = document.getElementById("downloadFailedModalMessage");
|
|
@@ -1804,6 +1917,48 @@ function closeDownloadPlayersModal() {
|
|
|
1804
1917
|
document.getElementById("downloadPlayersModal")?.classList.add("hidden");
|
|
1805
1918
|
}
|
|
1806
1919
|
|
|
1920
|
+
function setUpdateSdkVersionLabel(version) {
|
|
1921
|
+
const label = document.getElementById("updateSdkVersionLabel");
|
|
1922
|
+
if (!label) return;
|
|
1923
|
+
label.textContent = version || "unknown";
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
async function openUpdateSdkModal() {
|
|
1927
|
+
const modal = document.getElementById("updateSdkModal");
|
|
1928
|
+
const promptEl = document.getElementById("updateSdkPromptText");
|
|
1929
|
+
if (promptEl) promptEl.textContent = UPDATE_SDK_PROMPT;
|
|
1930
|
+
setUpdateSdkVersionLabel(cachedSdkVersion || "Loading…");
|
|
1931
|
+
modal?.classList.remove("hidden");
|
|
1932
|
+
|
|
1933
|
+
if (!cachedSdkVersion) {
|
|
1934
|
+
await fetchServerStatus();
|
|
1935
|
+
setUpdateSdkVersionLabel(cachedSdkVersion || "unknown");
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
function closeUpdateSdkModal() {
|
|
1940
|
+
document.getElementById("updateSdkModal")?.classList.add("hidden");
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
async function copyUpdateSdkPrompt() {
|
|
1944
|
+
const text =
|
|
1945
|
+
document.getElementById("updateSdkPromptText")?.textContent?.trim() ||
|
|
1946
|
+
UPDATE_SDK_PROMPT;
|
|
1947
|
+
try {
|
|
1948
|
+
await navigator.clipboard.writeText(text);
|
|
1949
|
+
const btn = document.getElementById("copyUpdateSdkPromptBtn");
|
|
1950
|
+
if (btn) {
|
|
1951
|
+
const prev = btn.textContent;
|
|
1952
|
+
btn.textContent = "Copied";
|
|
1953
|
+
setTimeout(() => {
|
|
1954
|
+
btn.textContent = prev || "Copy prompt";
|
|
1955
|
+
}, 1200);
|
|
1956
|
+
}
|
|
1957
|
+
} catch {
|
|
1958
|
+
alert("Could not copy automatically. Select the prompt text and copy it manually.");
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1807
1962
|
function handlePlayerDownloadLinkClick(ev) {
|
|
1808
1963
|
ev.preventDefault();
|
|
1809
1964
|
const platform = ev.currentTarget?.dataset?.playerDownload || "unknown";
|
|
@@ -1985,6 +2140,9 @@ async function fetchServerStatus() {
|
|
|
1985
2140
|
});
|
|
1986
2141
|
return;
|
|
1987
2142
|
}
|
|
2143
|
+
if (typeof data.sdkVersion === "string" && data.sdkVersion.trim()) {
|
|
2144
|
+
cachedSdkVersion = data.sdkVersion.trim();
|
|
2145
|
+
}
|
|
1988
2146
|
renderServerStatus(data);
|
|
1989
2147
|
} catch (err) {
|
|
1990
2148
|
renderServerStatus({
|
|
@@ -2051,6 +2209,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
2051
2209
|
document.querySelectorAll("[data-close-screenshot-modal]").forEach((el) => {
|
|
2052
2210
|
el.addEventListener("click", closeScreenshotModal);
|
|
2053
2211
|
});
|
|
2212
|
+
document.querySelectorAll("[data-close-on-off-timer-modal]").forEach((el) => {
|
|
2213
|
+
el.addEventListener("click", closeOnOffTimerModal);
|
|
2214
|
+
});
|
|
2215
|
+
document
|
|
2216
|
+
.getElementById("onOffTimerSaveBtn")
|
|
2217
|
+
?.addEventListener("click", () => void saveOnOffTimerModal());
|
|
2054
2218
|
document.querySelectorAll("[data-close-download-failed-modal]").forEach((el) => {
|
|
2055
2219
|
el.addEventListener("click", closeDownloadFailedModal);
|
|
2056
2220
|
});
|
|
@@ -2063,6 +2227,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
2063
2227
|
el.addEventListener("click", handlePlayerDownloadLinkClick);
|
|
2064
2228
|
});
|
|
2065
2229
|
|
|
2230
|
+
document.getElementById("updateSdkBtn")?.addEventListener("click", () => void openUpdateSdkModal());
|
|
2231
|
+
document.querySelectorAll("[data-close-update-sdk-modal]").forEach((el) => {
|
|
2232
|
+
el.addEventListener("click", closeUpdateSdkModal);
|
|
2233
|
+
});
|
|
2234
|
+
document
|
|
2235
|
+
.getElementById("copyUpdateSdkPromptBtn")
|
|
2236
|
+
?.addEventListener("click", () => void copyUpdateSdkPrompt());
|
|
2237
|
+
|
|
2066
2238
|
document.getElementById("addAssetBtn")?.addEventListener("click", () => {
|
|
2067
2239
|
if (uploadInProgress) return;
|
|
2068
2240
|
if (!isPlaylistEditorOpen()) {
|
|
@@ -829,6 +829,30 @@ button.danger {
|
|
|
829
829
|
width: min(94vw, 860px);
|
|
830
830
|
}
|
|
831
831
|
|
|
832
|
+
.on-off-timer-enable {
|
|
833
|
+
display: flex;
|
|
834
|
+
align-items: center;
|
|
835
|
+
gap: 0.5rem;
|
|
836
|
+
margin: 0.85rem 0 0.65rem;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
.on-off-timer-enable input {
|
|
840
|
+
width: auto;
|
|
841
|
+
margin: 0;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.on-off-timer-grid {
|
|
845
|
+
display: grid;
|
|
846
|
+
grid-template-columns: 1fr 1fr;
|
|
847
|
+
gap: 0.75rem;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
@media (max-width: 520px) {
|
|
851
|
+
.on-off-timer-grid {
|
|
852
|
+
grid-template-columns: 1fr;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
832
856
|
.screenshot-preview {
|
|
833
857
|
display: block;
|
|
834
858
|
width: 100%;
|
|
@@ -908,7 +932,9 @@ button.danger {
|
|
|
908
932
|
.download-players-nav-slot {
|
|
909
933
|
margin-top: auto;
|
|
910
934
|
display: flex;
|
|
911
|
-
|
|
935
|
+
flex-direction: column;
|
|
936
|
+
align-items: stretch;
|
|
937
|
+
gap: 0.45rem;
|
|
912
938
|
padding-top: 0.75rem;
|
|
913
939
|
}
|
|
914
940
|
|
|
@@ -922,12 +948,43 @@ button.danger {
|
|
|
922
948
|
background: #fff;
|
|
923
949
|
color: #0a0908;
|
|
924
950
|
cursor: pointer;
|
|
951
|
+
width: 100%;
|
|
952
|
+
text-align: center;
|
|
925
953
|
}
|
|
926
954
|
|
|
927
955
|
.download-players-trigger:hover {
|
|
928
956
|
background: #f5f3ef;
|
|
929
957
|
}
|
|
930
958
|
|
|
959
|
+
.update-sdk-modal-card {
|
|
960
|
+
width: min(92vw, 480px);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
.update-sdk-version {
|
|
964
|
+
margin: 0 0 1rem;
|
|
965
|
+
font-size: 1.35rem;
|
|
966
|
+
font-weight: 600;
|
|
967
|
+
letter-spacing: 0.02em;
|
|
968
|
+
color: #0a0908;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
.update-sdk-prompt {
|
|
972
|
+
margin: 0;
|
|
973
|
+
padding: 0.75rem 0.85rem;
|
|
974
|
+
border: 1px solid #e4e1dc;
|
|
975
|
+
border-radius: 8px;
|
|
976
|
+
background: #fafaf9;
|
|
977
|
+
font-size: 0.8rem;
|
|
978
|
+
line-height: 1.45;
|
|
979
|
+
white-space: pre-wrap;
|
|
980
|
+
word-break: break-word;
|
|
981
|
+
color: #0a0908;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
.update-sdk-modal-actions {
|
|
985
|
+
justify-content: flex-end;
|
|
986
|
+
}
|
|
987
|
+
|
|
931
988
|
.download-players-modal-card {
|
|
932
989
|
width: min(92vw, 560px);
|
|
933
990
|
padding: 1.5rem 1.75rem 1.25rem;
|