From 086398dde1deeb79af8d1db01a5197e668a5d561 Mon Sep 17 00:00:00 2001 From: Luke Hoersten Date: Sun, 14 Jun 2026 21:00:21 -0500 Subject: scrypted: persist display_name across script reloads + prefer it over v.name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script-reload re-discovery passed `name: nativeId` to onDeviceDiscovered, which Scrypted honored by renaming the existing "kitchen" device to its vp_xxx nativeId. The subsequent registerViewport fallback then saw a non-empty `v.name` (the nativeId) and used it as the viewport name when POSTing /config, sending the firmware /config with viewport="vp_mqek8i55_3z3s" instead of "kitchen". Fix two things: 1. start()'s re-discovery now passes the persisted display_name (set on createDevice + every Settings save) as the device's `name`, so the user-chosen kitchen name survives script reloads. 2. registerViewport now prefers storage's display_name over v.name — the storage value is the authoritative one, v.name is just a render that can briefly drift to the nativeId during reload. --- scrypted/scrypted-viewport.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/scrypted/scrypted-viewport.ts b/scrypted/scrypted-viewport.ts index 3a95a69..69ccd70 100644 --- a/scrypted/scrypted-viewport.ts +++ b/scrypted/scrypted-viewport.ts @@ -274,10 +274,15 @@ class ScryptedViewportProvider extends ScryptedDeviceBase // event subscription happen at plugin load. for (const nativeId of this.childIds) { try { + // Use the persisted display_name as the canonical device + // name so a script reload doesn't reset it to the nativeId. + // First-time provision falls back to the nativeId. + const displayName = + deviceManager.getDeviceStorage(nativeId).getItem("display_name") || nativeId; await deviceManager.onDeviceDiscovered({ providerNativeId: this.nativeId, nativeId, - name: nativeId, // overridden by Scrypted from its existing record + name: displayName, type: ScryptedDeviceType.SmartDisplay, interfaces: [ScryptedInterface.Settings], }); @@ -434,12 +439,13 @@ class ScryptedViewportProvider extends ScryptedDeviceBase } private async registerViewport(v: Viewport) { - // Guard against transient empty names. Scrypted occasionally hands - // us a Viewport whose `.name` hasn't resolved yet (race between - // device-record load and event delivery); POSTing /config with an - // empty viewport just gets a 400 from the firmware. Fall back to - // the stored display name from storage if it's there, else skip. - const name = (v.name && v.name.trim()) || v.storage.getItem("display_name") || ""; + // display_name is the canonical user-facing name (written on + // createDevice and on every Settings save). v.name is just a + // render of it from the Scrypted device record and can briefly + // drift to the nativeId on script reload, so prefer the storage + // value as the source of truth. + const stored = v.storage.getItem("display_name"); + const name = (stored && stored.trim()) || (v.name && v.name.trim()) || ""; if (!name) { this.console.warn(`register skipped — empty name on ${v.nativeId}; will retry on next event`); return; -- cgit v1.2.3