From 3900a804f0ec55f7799eb01b63c82466e4501408 Mon Sep 17 00:00:00 2001 From: Krisjanis Lejejs Date: Thu, 6 Aug 2026 09:42:29 +0300 Subject: [PATCH] Remove cloud demo controls (#53524) --- demo/src/cloud/cloud-demo-controls.ts | 304 -------------------------- demo/src/ha-demo.ts | 6 - demo/src/stubs/backup.ts | 134 +++--------- demo/src/stubs/cloud-demo-state.ts | 89 -------- demo/src/stubs/cloud.ts | 131 ++--------- 5 files changed, 49 insertions(+), 615 deletions(-) delete mode 100644 demo/src/cloud/cloud-demo-controls.ts delete mode 100644 demo/src/stubs/cloud-demo-state.ts diff --git a/demo/src/cloud/cloud-demo-controls.ts b/demo/src/cloud/cloud-demo-controls.ts deleted file mode 100644 index 36b00ac545..0000000000 --- a/demo/src/cloud/cloud-demo-controls.ts +++ /dev/null @@ -1,304 +0,0 @@ -import { mdiClose, mdiFlaskOutline } from "@mdi/js"; -import { css, html, LitElement, nothing } from "lit"; -import { customElement, state } from "lit/decorators"; -import { fireEvent } from "../../../src/common/dom/fire_event"; -import { mainWindow } from "../../../src/common/dom/get_main_window"; -import { navigate } from "../../../src/common/navigate"; -import "../../../src/components/ha-button"; -import "../../../src/components/ha-card"; -import "../../../src/components/ha-icon-button"; -import "../../../src/components/ha-svg-icon"; -import "../../../src/components/ha-switch"; -import type { HaSwitch } from "../../../src/components/ha-switch"; -import type { CloudDemoScenario } from "../stubs/cloud-demo-state"; -import { - getCloudDemoScenario, - setCloudDemoScenario, - subscribeCloudDemoScenario, -} from "../stubs/cloud-demo-state"; - -// Walk the DOM, descending into shadow roots, to find the first matching -// element. Used to reach (which owns the cloud status) so we -// can ask it to re-fetch after a scenario change. -const deepQuery = ( - selector: string, - root: Document | ShadowRoot = document -): Element | null => { - const direct = root.querySelector(selector); - if (direct) { - return direct; - } - const elements = root.querySelectorAll("*"); - for (const element of elements) { - const shadow = element.shadowRoot; - if (shadow) { - const found = deepQuery(selector, shadow); - if (found) { - return found; - } - } - } - return null; -}; - -/** - * Demo-only floating panel that flips the mocked Home Assistant Cloud state so - * reviewers can preview every UI state of the cloud account page. It writes to - * the shared {@link CloudDemoScenario} (which the cloud/backup mocks read) and - * then nudges the page to re-read it. Lives entirely under demo/. - */ -@customElement("cloud-demo-controls") -export class CloudDemoControls extends LitElement { - @state() private _open = true; - - @state() private _visible = false; - - @state() private _scenario: CloudDemoScenario = getCloudDemoScenario(); - - private _unsub?: () => void; - - // The demo uses hash-based routing (navigate() sets location.hash), so the - // active route lives in the hash, not the pathname. - private get _currentPath(): string { - const hash = mainWindow.location.hash; - return hash.startsWith("#/") ? hash.slice(1) : mainWindow.location.pathname; - } - - private _locationChanged = () => { - this._visible = this._currentPath.startsWith("/config/cloud"); - }; - - public connectedCallback(): void { - super.connectedCallback(); - this._locationChanged(); - mainWindow.addEventListener("location-changed", this._locationChanged); - mainWindow.addEventListener("popstate", this._locationChanged); - mainWindow.addEventListener("hashchange", this._locationChanged); - this._unsub = subscribeCloudDemoScenario((scenario) => { - this._scenario = { ...scenario }; - }); - } - - public disconnectedCallback(): void { - super.disconnectedCallback(); - mainWindow.removeEventListener("location-changed", this._locationChanged); - mainWindow.removeEventListener("popstate", this._locationChanged); - mainWindow.removeEventListener("hashchange", this._locationChanged); - this._unsub?.(); - } - - protected render() { - if (!this._visible) { - return nothing; - } - if (!this._open) { - return html` - - `; - } - return html` - -
- - Cloud demo controls - -
-

- Demo only. Flips the mocked cloud state shown on this page. -

-
- ${this._segment("Subscription", "account", [ - ["active", "Active"], - ["trialing", "Trialing"], - ["canceled", "Canceled"], - ["expired", "Expired"], - ["unknown", "Unknown"], - ])} - ${this._toggle("Onboarded", "onboarded")} - ${this._toggle("Onboarding postponed", "postponed")} - ${this._toggle("Remote access", "remote")} - ${this._segment("Remote status", "remoteStatus", [ - ["ready", "Ready"], - ["generating", "Preparing"], - ["loading", "Loading"], - ["loaded", "Loaded"], - ["error", "Error"], - ])} - ${this._segment("Backups", "backup", [ - ["fresh", "Recent"], - ["stale", "Old"], - ["failed", "Failed"], - ["local", "Local only"], - ["none", "None"], - ])} - ${this._toggle("Alexa linked", "alexa")} - ${this._toggle("Google linked", "google")} - ${this._toggle("Cameras (WebRTC)", "webrtc")} - ${this._toggle("Has webhooks", "webhooks")} -
-
- `; - } - - private _segment( - label: string, - field: keyof CloudDemoScenario, - options: [string, string][] - ) { - return html` -
- ${label} -
- ${options.map( - ([value, text]) => html` - - ${text} - - ` - )} -
-
- `; - } - - private _toggle(label: string, field: keyof CloudDemoScenario) { - return html` -
- ${label} - -
- `; - } - - private _toggleOpen() { - this._open = !this._open; - } - - private _segmentClick(ev: Event) { - const target = ev.currentTarget as HTMLElement; - this._set( - target.dataset.field as keyof CloudDemoScenario, - target.dataset.value! - ); - } - - private _toggleChange(ev: Event) { - const target = ev.target as HaSwitch; - this._set(target.dataset.field as keyof CloudDemoScenario, target.checked); - } - - private _set(field: keyof CloudDemoScenario, value: string | boolean) { - setCloudDemoScenario({ [field]: value } as Partial); - this._refresh(); - } - - private _refresh() { - // Refresh the shared cloud status so login-state changes (signed out) and - // status-derived fields update. - const panel = deepQuery("ha-panel-config"); - if (panel) { - fireEvent(panel as HTMLElement, "ha-refresh-cloud-status"); - } - // cloud-account fetches its subscription/backup/webhook data once on mount - // and is not cached by the router, so bounce through a sibling cloud route - // to force a clean remount that re-reads the updated mocks. - const path = this._currentPath; - if (path.startsWith("/config/cloud") && path !== "/config/cloud/login") { - const sibling = - path === "/config/cloud/remote" - ? "/config/cloud/account" - : "/config/cloud/remote"; - navigate(sibling, { replace: true }); - window.setTimeout(() => navigate(path, { replace: true }), 0); - } - } - - static styles = css` - :host { - position: fixed; - right: 16px; - bottom: 16px; - z-index: 9999; - } - .fab { - --mdc-icon-button-size: 48px; - --mdc-icon-size: 24px; - background-color: var(--primary-color); - color: var(--text-primary-color, #fff); - border-radius: 50%; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); - } - ha-card { - display: block; - width: 320px; - max-height: 80vh; - overflow: auto; - box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3); - } - .header { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 8px 8px 16px; - border-bottom: 1px solid var(--divider-color); - } - .header .title { - flex: 1; - font-weight: var(--ha-font-weight-medium, 500); - } - .header ha-svg-icon { - color: var(--secondary-text-color); - } - .note { - margin: 8px 16px; - color: var(--secondary-text-color); - font-size: var(--ha-font-size-s, 0.875rem); - } - .controls { - display: flex; - flex-direction: column; - gap: 8px; - padding: 0 16px 16px; - } - .row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - min-height: 36px; - } - .segment { - display: flex; - gap: 4px; - flex-wrap: wrap; - justify-content: flex-end; - } - `; -} - -declare global { - interface HTMLElementTagNameMap { - "cloud-demo-controls": CloudDemoControls; - } -} diff --git a/demo/src/ha-demo.ts b/demo/src/ha-demo.ts index 0118978d67..1c974393f8 100644 --- a/demo/src/ha-demo.ts +++ b/demo/src/ha-demo.ts @@ -32,7 +32,6 @@ import { mockTemplate } from "./stubs/template"; import { mockTodo } from "./stubs/todo"; import { mockTranslations } from "./stubs/translations"; import { mockUsagePrediction } from "./stubs/usage_prediction"; -import "./cloud/cloud-demo-controls"; // WS command / REST path prefixes whose mocks live in the lazily imported // config-panel chunk (see ./stubs/config-panel). Must stay in sync with it. @@ -92,11 +91,6 @@ export class HaDemo extends HomeAssistantAppEl { }, }); - // Demo-only floating panel to flip the mocked cloud state. Mounted once at - // the document level; it shows itself only on the cloud panel. - if (!document.querySelector("cloud-demo-controls")) { - document.body.appendChild(document.createElement("cloud-demo-controls")); - } const localizePromise = // @ts-ignore this._loadFragmentTranslations(hass.language, "page-demo").then( diff --git a/demo/src/stubs/backup.ts b/demo/src/stubs/backup.ts index 885453c76d..d49b576d10 100644 --- a/demo/src/stubs/backup.ts +++ b/demo/src/stubs/backup.ts @@ -7,31 +7,43 @@ import type { import { BackupScheduleRecurrence } from "../../../src/data/backup"; import type { ManagerStateEvent } from "../../../src/data/backup_manager"; import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; -import type { DemoCloudBackup } from "./cloud-demo-state"; -import { - getCloudDemoScenario, - setCloudDemoScenario, - subscribeCloudDemoScenario, -} from "./cloud-demo-state"; const CLOUD_AGENT = "cloud.cloud"; +// Fixed "recent" backup state: an automatic backup completed 12h ago to both the +// local and cloud agents, with the next run scheduled for tomorrow. This is the +// healthy state the cloud overview status line and backup sub-page render. +const now = Date.now(); +const recent = new Date(now - 12 * 3600 * 1000).toISOString(); +const future = new Date(now + 86400000).toISOString(); + const backupInfo: BackupInfo = { - backups: [], + backups: [ + { + backup_id: "demo-backup-1", + name: "Automatic backup DEMO", + date: recent, + with_automatic_settings: true, + agents: { + "backup.local": { size: 1024 * 1024 * 512, protected: true }, + "cloud.cloud": { size: 1024 * 1024 * 512, protected: true }, + }, + } as BackupContent, + ], agent_errors: {}, - last_attempted_automatic_backup: null, - last_completed_automatic_backup: null, + last_attempted_automatic_backup: recent, + last_completed_automatic_backup: recent, last_action_event: { manager_state: "idle" }, - next_automatic_backup: null, + next_automatic_backup: future, next_automatic_backup_additional: false, state: "idle", }; const backupConfig: BackupConfig = { automatic_backups_configured: true, - last_attempted_automatic_backup: null, - last_completed_automatic_backup: null, - next_automatic_backup: null, + last_attempted_automatic_backup: recent, + last_completed_automatic_backup: recent, + next_automatic_backup: future, next_automatic_backup_additional: false, create_backup: { agent_ids: ["backup.local", CLOUD_AGENT], @@ -61,88 +73,6 @@ const agentsInfo: BackupAgentsInfo = { ], }; -// Map the demo "Backups" scenario onto the mutable backup config/info, so the -// cloud overview status line and the backup sub-page reflect the chosen state. -const applyScenario = () => { - const kind = getCloudDemoScenario().backup; - const now = Date.now(); - const recent = new Date(now - 12 * 3600 * 1000).toISOString(); - const old = new Date(now - 5 * 86400000).toISOString(); - const future = new Date(now + 86400000).toISOString(); - // Comfortably past BACKUP_OVERDUE_MARGIN_HOURS (3h) so the "stale" scenario - // actually reads as overdue rather than slipping under the margin. - const overdue = new Date(now - 6 * 3600 * 1000).toISOString(); - - // The cloud agent is a backup target for the cloud-backed states only. For - // "local" a backup exists but is stored locally (no cloud copy), and for - // "none" there are no automatic backups at all. - const cloudEnabled = - kind === "fresh" || kind === "stale" || kind === "failed"; - backupConfig.create_backup.agent_ids = cloudEnabled - ? ["backup.local", CLOUD_AGENT] - : ["backup.local"]; - - switch (kind) { - case "fresh": - backupConfig.automatic_backups_configured = true; - backupConfig.last_completed_automatic_backup = recent; - backupConfig.last_attempted_automatic_backup = recent; - backupConfig.next_automatic_backup = future; - break; - case "local": - // Automatic backups run, but only to the local agent. - backupConfig.automatic_backups_configured = true; - backupConfig.last_completed_automatic_backup = recent; - backupConfig.last_attempted_automatic_backup = recent; - backupConfig.next_automatic_backup = future; - break; - case "stale": - backupConfig.automatic_backups_configured = true; - backupConfig.last_completed_automatic_backup = old; - backupConfig.last_attempted_automatic_backup = old; - // Next scheduled backup is in the past, so it reads as overdue. - backupConfig.next_automatic_backup = overdue; - break; - case "failed": - backupConfig.automatic_backups_configured = true; - backupConfig.last_completed_automatic_backup = old; - // Most recent attempt is newer than the last success, so it failed. - backupConfig.last_attempted_automatic_backup = recent; - backupConfig.next_automatic_backup = future; - break; - case "none": - backupConfig.automatic_backups_configured = false; - backupConfig.last_completed_automatic_backup = null; - backupConfig.last_attempted_automatic_backup = null; - backupConfig.next_automatic_backup = null; - break; - } - - backupInfo.last_completed_automatic_backup = - backupConfig.last_completed_automatic_backup; - backupInfo.last_attempted_automatic_backup = - backupConfig.last_attempted_automatic_backup; - backupInfo.next_automatic_backup = backupConfig.next_automatic_backup; - backupInfo.backups = - cloudEnabled && backupConfig.last_completed_automatic_backup - ? [ - { - backup_id: "demo-backup-1", - name: "Automatic backup DEMO", - date: backupConfig.last_completed_automatic_backup, - with_automatic_settings: true, - agents: { - "backup.local": { size: 1024 * 1024 * 512, protected: true }, - "cloud.cloud": { size: 1024 * 1024 * 512, protected: true }, - }, - } as BackupContent, - ] - : []; -}; - -applyScenario(); -subscribeCloudDemoScenario(applyScenario); - export const mockBackup = (hass: MockHomeAssistant) => { // Fresh objects each fetch so re-reading after a mutation actually re-renders // (Lit change detection is identity-based; the real WS API returns new @@ -171,20 +101,6 @@ export const mockBackup = (hass: MockHomeAssistant) => { if (update.agents) { backupConfig.agents = { ...backupConfig.agents, ...update.agents }; } - // Reflect the UI-driven backup change into the demo scenario so the demo - // controls panel stays in sync with the mocked state. - const cloudNow = backupConfig.create_backup.agent_ids.includes(CLOUD_AGENT); - const current = getCloudDemoScenario().backup; - const next: DemoCloudBackup = !backupConfig.automatic_backups_configured - ? "none" - : cloudNow - ? current === "fresh" || current === "stale" || current === "failed" - ? current - : "fresh" - : "local"; - if (next !== current) { - setCloudDemoScenario({ backup: next }); - } return null; }); hass.mockWS( diff --git a/demo/src/stubs/cloud-demo-state.ts b/demo/src/stubs/cloud-demo-state.ts deleted file mode 100644 index 26cea90d12..0000000000 --- a/demo/src/stubs/cloud-demo-state.ts +++ /dev/null @@ -1,89 +0,0 @@ -// Demo-only switchable Home Assistant Cloud scenario. -// -// The redesigned cloud account page (src/panels/config/cloud/account) renders -// purely from real WS data. To let reviewers preview every UI state without a -// real cloud account, this module holds a mutable "scenario" that the cloud and -// backup mocks read from, plus the floating panel writes -// to. It is persisted to localStorage so the choice survives the data the page -// fetches once per visit (subscription, backup config, webhooks). -// -// This lives entirely under demo/ — no production code imports it. - -import type { RemoteCertificateStatus } from "../../../src/data/cloud"; - -// The five PaymentSubscriptionState values. -export type DemoCloudAccount = - "active" | "trialing" | "canceled" | "expired" | "unknown"; - -// "local": automatic backups are configured, but not to the cloud agent -// (a backup exists, just no cloud copy). "none": no automatic backups at all. -export type DemoCloudBackup = "fresh" | "stale" | "failed" | "local" | "none"; - -export interface CloudDemoScenario { - account: DemoCloudAccount; - onboarded: boolean; - // Onboarding postponed server-side (maps to onboarding_postponed); hides - // the onboarding UI without marking it completed. - postponed: boolean; - remote: boolean; - remoteStatus: RemoteCertificateStatus; - backup: DemoCloudBackup; - alexa: boolean; - google: boolean; - webrtc: boolean; - webhooks: boolean; -} - -export const DEFAULT_CLOUD_DEMO_SCENARIO: CloudDemoScenario = { - account: "active", - onboarded: true, - postponed: false, - remote: true, - remoteStatus: "ready", - backup: "fresh", - alexa: true, - google: true, - webrtc: true, - webhooks: true, -}; - -const STORAGE_KEY = "cloudDemoScenario"; - -const readScenario = (): CloudDemoScenario => { - try { - const raw = window.localStorage.getItem(STORAGE_KEY); - if (raw) { - return { ...DEFAULT_CLOUD_DEMO_SCENARIO, ...JSON.parse(raw) }; - } - } catch (_err) { - // Ignore malformed or unavailable storage and fall back to the default. - } - return { ...DEFAULT_CLOUD_DEMO_SCENARIO }; -}; - -let scenario: CloudDemoScenario = readScenario(); - -const listeners = new Set<(scenario: CloudDemoScenario) => void>(); - -export const getCloudDemoScenario = (): CloudDemoScenario => scenario; - -export const subscribeCloudDemoScenario = ( - listener: (scenario: CloudDemoScenario) => void -): (() => void) => { - listeners.add(listener); - return () => { - listeners.delete(listener); - }; -}; - -export const setCloudDemoScenario = ( - partial: Partial -): void => { - scenario = { ...scenario, ...partial }; - try { - window.localStorage.setItem(STORAGE_KEY, JSON.stringify(scenario)); - } catch (_err) { - // Ignore storage failures (e.g. private mode); state still applies in-memory. - } - listeners.forEach((listener) => listener(scenario)); -}; diff --git a/demo/src/stubs/cloud.ts b/demo/src/stubs/cloud.ts index e01ed0bf30..6716259e88 100644 --- a/demo/src/stubs/cloud.ts +++ b/demo/src/stubs/cloud.ts @@ -6,11 +6,6 @@ import { ONBOARDING_ITEMS } from "../../../src/data/cloud"; import type { CloudTTSInfo } from "../../../src/data/cloud/tts"; import type { Webhook } from "../../../src/data/webhook"; import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; -import { - getCloudDemoScenario, - setCloudDemoScenario, - subscribeCloudDemoScenario, -} from "./cloud-demo-state"; const emptyFilter = () => ({ include_domains: [], @@ -34,14 +29,30 @@ const demoWebhooks: Webhook[] = [ }, ]; -// A single mutable status object so that preference changes made in the demo -// (both via the real UI and the demo scenario controls) are reflected back. +const demoCloudhooks = Object.fromEntries( + demoWebhooks.map((webhook) => [ + webhook.webhook_id, + { + webhook_id: webhook.webhook_id, + cloudhook_id: `demo-${webhook.webhook_id}`, + cloudhook_url: `https://hooks.nabu.casa/demo-${webhook.webhook_id}`, + managed: false, + }, + ]) +); + +// A single mutable status object seeded to one fixed demo state: an active cloud +// subscription with remote ready, cloud backups recent, webhooks set up and voice +// set up (Alexa linked), but cameras (WebRTC) off, so onboarding is still in +// progress (streaming is the remaining step) and not postponed. Page-driven +// changes (connect remote, postpone onboarding, add/delete webhooks) mutate this +// object directly and reset on reload. const cloudStatus: CloudStatusLoggedIn = { logged_in: true, cloud: "connected", cloud_last_disconnect_reason: null, email: "demo@home-assistant.io", - google_registered: true, + google_registered: false, google_entities: emptyFilter(), google_domains: ["light", "switch", "climate", "cover"], alexa_registered: true, @@ -58,20 +69,20 @@ const cloudStatus: CloudStatusLoggedIn = { http_use_ssl: false, active_subscription: true, onboarding_postponed: false, - onboarding_completed: true, + onboarding_completed: false, prefs: { - google_enabled: true, + google_enabled: false, alexa_enabled: true, remote_enabled: true, remote_allow_remote_enable: true, strict_connection: "disabled", google_secure_devices_pin: undefined, - cloudhooks: {}, + cloudhooks: demoCloudhooks, alexa_report_state: true, google_report_state: true, tts_default_voice: ["en-US", "JennyNeural"], - cloud_ice_servers_enabled: true, - onboarded_items: [...ONBOARDING_ITEMS], + cloud_ice_servers_enabled: false, + onboarded_items: [], onboarding_postponed_until: null, }, }; @@ -93,94 +104,6 @@ const ttsInfo: CloudTTSInfo = { ], }; -// Map the high-level demo scenario onto the mutable cloud status / subscription. -const applyScenario = () => { - const scenario = getCloudDemoScenario(); - - switch (scenario.account) { - case "trialing": - cloudStatus.active_subscription = true; - subscription.subscription = { status: "trialing" }; - break; - case "canceled": - cloudStatus.active_subscription = false; - subscription.subscription = { status: "canceled" }; - break; - case "expired": - cloudStatus.active_subscription = false; - subscription.subscription = { status: "expired" }; - break; - case "unknown": - cloudStatus.active_subscription = true; - subscription.subscription = { status: "unknown" }; - break; - default: - // "active" - cloudStatus.active_subscription = true; - subscription.subscription = { status: "active" }; - } - - cloudStatus.prefs.onboarded_items = scenario.onboarded - ? [...ONBOARDING_ITEMS] - : []; - cloudStatus.onboarding_completed = scenario.onboarded; - cloudStatus.onboarding_postponed = scenario.postponed; - cloudStatus.prefs.onboarding_postponed_until = scenario.postponed - ? new Date(Date.now() + 24 * 3600 * 1000).toISOString() - : null; - cloudStatus.prefs.remote_enabled = scenario.remote; - cloudStatus.remote_connected = scenario.remote; - cloudStatus.remote_certificate_status = scenario.remoteStatus; - cloudStatus.alexa_registered = scenario.alexa; - cloudStatus.google_registered = scenario.google; - cloudStatus.prefs.cloud_ice_servers_enabled = scenario.webrtc; - - const hasCloudhooks = Object.keys(cloudStatus.prefs.cloudhooks).length > 0; - if (scenario.webhooks && !hasCloudhooks) { - cloudStatus.prefs.cloudhooks = Object.fromEntries( - demoWebhooks.map((webhook) => [ - webhook.webhook_id, - { - webhook_id: webhook.webhook_id, - cloudhook_id: `demo-${webhook.webhook_id}`, - cloudhook_url: `https://hooks.nabu.casa/demo-${webhook.webhook_id}`, - managed: false, - }, - ]) - ); - } else if (!scenario.webhooks && hasCloudhooks) { - cloudStatus.prefs.cloudhooks = {}; - } -}; - -applyScenario(); -subscribeCloudDemoScenario(applyScenario); - -// Reflect UI-driven changes (onboarding toggles, remote connect/disconnect) -// back into the demo scenario so the demo controls panel stays in sync with the -// mocked state. Only writes when a value actually changed, to avoid needless -// re-projection. `applyScenario` re-applies the (now matching) scenario, so -// this stays idempotent and does not fight the direct mutation above. -const syncScenarioFromStatus = () => { - const scenario = getCloudDemoScenario(); - const next = { - onboarded: cloudStatus.onboarding_completed, - postponed: cloudStatus.onboarding_postponed, - remote: cloudStatus.prefs.remote_enabled, - webrtc: cloudStatus.prefs.cloud_ice_servers_enabled, - webhooks: Object.keys(cloudStatus.prefs.cloudhooks).length > 0, - }; - if ( - scenario.onboarded !== next.onboarded || - scenario.postponed !== next.postponed || - scenario.remote !== next.remote || - scenario.webrtc !== next.webrtc || - scenario.webhooks !== next.webhooks - ) { - setCloudDemoScenario(next); - } -}; - export const mockCloud = (hass: MockHomeAssistant) => { hass.mockWS("cloud/status", () => ({ ...cloudStatus, @@ -193,7 +116,6 @@ export const mockCloud = (hass: MockHomeAssistant) => { hass.mockWS("cloud/update_prefs", (msg) => { const { type, ...prefs } = msg; cloudStatus.prefs = { ...cloudStatus.prefs, ...prefs }; - syncScenarioFromStatus(); return { success: true }; }); @@ -202,7 +124,6 @@ export const mockCloud = (hass: MockHomeAssistant) => { Date.now() + 24 * 3600 * 1000 ).toISOString(); cloudStatus.onboarding_postponed = true; - syncScenarioFromStatus(); // Backend returns the full logged-in status object. return { ...cloudStatus, prefs: { ...cloudStatus.prefs } }; }); @@ -222,7 +143,6 @@ export const mockCloud = (hass: MockHomeAssistant) => { (onboardingItem) => cloudStatus.prefs.onboarded_items.includes(onboardingItem) ); - syncScenarioFromStatus(); // Backend returns the full logged-in status object. return { ...cloudStatus, prefs: { ...cloudStatus.prefs } }; }); @@ -245,20 +165,17 @@ export const mockCloud = (hass: MockHomeAssistant) => { const cloudhooks = { ...cloudStatus.prefs.cloudhooks }; delete cloudhooks[msg.webhook_id]; cloudStatus.prefs.cloudhooks = cloudhooks; - syncScenarioFromStatus(); return null; }); hass.mockWS("cloud/remote/connect", () => { cloudStatus.remote_connected = true; cloudStatus.prefs.remote_enabled = true; - syncScenarioFromStatus(); return null; }); hass.mockWS("cloud/remote/disconnect", () => { cloudStatus.remote_connected = false; cloudStatus.prefs.remote_enabled = false; - syncScenarioFromStatus(); return null; });