diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 505e58726e..4488c4048c 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -1,5 +1,4 @@ import "@home-assistant/webawesome/dist/components/divider/divider"; -import { consume } from "@lit/context"; import { mdiAppleKeyboardCommand, mdiCog, @@ -24,7 +23,7 @@ import { import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; -import { customElement, property, query, state } from "lit/decorators"; +import { customElement, property, query } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { UndoRedoController } from "../../../common/controllers/undo-redo-controller"; import { fireEvent } from "../../../common/dom/fire_event"; @@ -57,7 +56,6 @@ import { } from "../../../data/automation"; import { substituteBlueprint } from "../../../data/blueprint"; import { validateConfig } from "../../../data/config"; -import { fullEntitiesContext } from "../../../data/context"; import { UNAVAILABLE } from "../../../data/entity/entity"; import { type EntityRegistryEntry, @@ -80,6 +78,7 @@ import { showAutomationModeDialog } from "./automation-mode-dialog/show-dialog-a import { showAutomationSaveDialog } from "./automation-save-dialog/show-dialog-automation-save"; import { showAutomationSaveTimeoutDialog } from "./automation-save-timeout-dialog/show-dialog-automation-save-timeout"; import "./blueprint-automation-editor"; +import type { EditorDomainHooks } from "./ha-automation-script-editor-mixin"; import { AutomationScriptEditorMixin, automationScriptEditorStyles, @@ -117,10 +116,6 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin = { + domain: "automation", + fetchFileConfig: fetchAutomationFileConfig, + normalizeConfig: normalizeAutomationConfig, + checkValidation: () => this._checkValidation(), + }; + private _undoRedoController = new UndoRedoController(this, { apply: (config) => this._applyUndoRedo(config), currentConfig: () => this.config!, @@ -144,9 +146,9 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin entity.platform === "automation" && entity.unique_id === this._newAutomationId @@ -576,7 +578,7 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin${alertText}`, - }); - goBack("/config"); - return; - } - const entity = this._entityRegistry.find( - (ent) => - ent.platform === "automation" && ent.unique_id === this.automationId - ); - if (entity) { - navigate(`/config/automation/show/${entity.entity_id}`, { - replace: true, - }); - return; - } - await showAlertDialog(this, { - text: this.hass.localize( - "ui.panel.config.automation.editor.load_error_not_editable" - ), - }); - goBack("/config"); - } - } - private _valueChanged(ev: ValueChangedEvent) { ev.stopPropagation(); diff --git a/src/panels/config/automation/ha-automation-script-editor-mixin.ts b/src/panels/config/automation/ha-automation-script-editor-mixin.ts index 845903dd62..588f20f456 100644 --- a/src/panels/config/automation/ha-automation-script-editor-mixin.ts +++ b/src/panels/config/automation/ha-automation-script-editor-mixin.ts @@ -3,11 +3,14 @@ import type { CSSResult, TemplateResult, LitElement } from "lit"; import { css, html } from "lit"; import { property, state } from "lit/decorators"; import { transform } from "../../../common/decorators/transform"; -import { goBack } from "../../../common/navigate"; +import { goBack, navigate } from "../../../common/navigate"; import { afterNextRender } from "../../../common/util/render-status"; import { fullEntitiesContext } from "../../../data/context"; import type { EntityRegistryEntry } from "../../../data/entity/entity_registry"; -import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../dialogs/generic/show-dialog-box"; import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog"; import type { Constructor, HomeAssistant, Route } from "../../../types"; import type { EntityRegistryUpdate } from "./automation-save-dialog/show-dialog-automation-save"; @@ -67,6 +70,13 @@ export const automationScriptEditorStyles: CSSResult = css` } `; +export interface EditorDomainHooks { + fetchFileConfig(hass: HomeAssistant, id: string): Promise; + normalizeConfig(raw: TConfig): TConfig; + checkValidation(): Promise; + domain: "automation" | "script"; +} + export const AutomationScriptEditorMixin = ( superClass: Constructor ) => { @@ -81,6 +91,10 @@ export const AutomationScriptEditorMixin = ( @property({ attribute: false }) public entityId: string | null = null; + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + entityRegistry!: EntityRegistryEntry[]; + @state() protected dirty = false; @state() protected errors?: string; @@ -115,6 +129,8 @@ export const AutomationScriptEditorMixin = ( protected entityRegistryUpdate?: EntityRegistryUpdate; + protected domainHooks!: EditorDomainHooks; + protected entityRegCreated?: ( value: PromiseLike | EntityRegistryEntry ) => void; @@ -194,6 +210,47 @@ export const AutomationScriptEditorMixin = ( protected confirmUnsavedChanged(): Promise { return Promise.resolve(true); } + + protected async loadConfig(id: string) { + const hooks = this.domainHooks; + const domain = hooks.domain; + try { + const config = await hooks.fetchFileConfig(this.hass, id); + this.dirty = false; + this.readOnly = false; + this.config = hooks.normalizeConfig(config); + hooks.checkValidation(); + } catch (err: any) { + if (err.status_code !== 404) { + const alertText = + err.body?.message || err.body || err.error || "Unknown error"; + await showAlertDialog(this, { + title: this.hass.localize( + `ui.panel.config.${domain}.editor.load_error_unknown`, + { err_no: err.status_code ?? "unknown" } + ), + text: html`
${alertText}
`, + }); + goBack("/config"); + return; + } + const entity = this.entityRegistry.find( + (ent) => ent.platform === domain && ent.unique_id === id + ); + if (entity) { + navigate(`/config/${domain}/show/${entity.entity_id}`, { + replace: true, + }); + return; + } + await showAlertDialog(this, { + text: this.hass.localize( + `ui.panel.config.${domain}.editor.load_error_not_editable` + ), + }); + goBack("/config"); + } + } } return AutomationScriptEditorClass; }; diff --git a/src/panels/config/script/ha-config-script.ts b/src/panels/config/script/ha-config-script.ts index 9be0b0f1e8..28940431ea 100644 --- a/src/panels/config/script/ha-config-script.ts +++ b/src/panels/config/script/ha-config-script.ts @@ -1,13 +1,10 @@ -import { consume } from "@lit/context"; import type { HassEntities } from "home-assistant-js-websocket"; import type { PropertyValues } from "lit"; -import { customElement, property, state } from "lit/decorators"; +import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { debounce } from "../../../common/util/debounce"; import type { CloudStatus } from "../../../data/cloud"; -import { fullEntitiesContext } from "../../../data/context"; -import type { EntityRegistryEntry } from "../../../data/entity/entity_registry"; import type { ScriptEntity } from "../../../data/script"; import type { RouterOptions } from "../../../layouts/hass-router-page"; import { HassRouterPage } from "../../../layouts/hass-router-page"; @@ -36,10 +33,6 @@ class HaConfigScript extends HassRouterPage { @property({ attribute: false }) public scripts: ScriptEntity[] = []; - @state() - @consume({ context: fullEntitiesContext, subscribe: true }) - _entityReg: EntityRegistryEntry[] = []; - protected routerOptions: RouterOptions = { defaultPage: "dashboard", routes: { @@ -86,7 +79,6 @@ class HaConfigScript extends HassRouterPage { pageEl.isWide = this.isWide; pageEl.route = this.routeTail; pageEl.showAdvanced = this.showAdvanced; - pageEl.entityRegistry = this._entityReg; pageEl.cloudStatus = this.cloudStatus; if (this.hass) { diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts index f12d14422a..65a36056ed 100644 --- a/src/panels/config/script/ha-script-editor.ts +++ b/src/panels/config/script/ha-script-editor.ts @@ -71,6 +71,7 @@ import { showAutomationSaveDialog } from "../automation/automation-save-dialog/s import { showAutomationSaveTimeoutDialog } from "../automation/automation-save-timeout-dialog/show-dialog-automation-save-timeout"; import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import "./blueprint-script-editor"; +import type { EditorDomainHooks } from "../automation/ha-automation-script-editor-mixin"; import { AutomationScriptEditorMixin, automationScriptEditorStyles, @@ -87,13 +88,18 @@ export class HaScriptEditor extends SubscribeMixin( ) { @property({ attribute: false }) public scriptId: string | null = null; - @property({ attribute: false }) public entityRegistry!: EntityRegistryEntry[]; - @query("manual-script-editor") private _manualEditor?: HaManualScriptEditor; private _newScriptId?: string; + protected domainHooks: EditorDomainHooks = { + domain: "script", + fetchFileConfig: fetchScriptFileConfig, + normalizeConfig: normalizeScriptConfig, + checkValidation: () => this._checkValidation(), + }; + private _undoRedoController = new UndoRedoController(this, { apply: (config) => this._applyUndoRedo(config), currentConfig: () => this.config!, @@ -480,6 +486,13 @@ export class HaScriptEditor extends SubscribeMixin( `; } + private _setEntityId() { + const entity = this.entityRegistry.find( + (ent) => ent.platform === "script" && ent.unique_id === this.scriptId + ); + this.currentEntityId = entity?.entity_id; + } + protected updated(changedProps: PropertyValues): void { super.updated(changedProps); @@ -492,7 +505,8 @@ export class HaScriptEditor extends SubscribeMixin( // Only refresh config if we picked a new script. If same ID, don't fetch it. (!oldScript || oldScript !== this.scriptId) ) { - this._loadConfig(); + this._setEntityId(); + this.loadConfig(this.scriptId); } if ( @@ -500,11 +514,7 @@ export class HaScriptEditor extends SubscribeMixin( this.scriptId && this.entityRegistry ) { - // find entity for when script entity id changed - const entity = this.entityRegistry.find( - (ent) => ent.platform === "script" && ent.unique_id === this.scriptId - ); - this.currentEntityId = entity?.entity_id; + this._setEntityId(); } if (changedProps.has("scriptId") && !this.scriptId && this.hass) { @@ -562,43 +572,6 @@ export class HaScriptEditor extends SubscribeMixin( ); } - private async _loadConfig() { - fetchScriptFileConfig(this.hass, this.scriptId!).then( - (config) => { - this.dirty = false; - this.readOnly = false; - this.config = normalizeScriptConfig(config); - const entity = this.entityRegistry.find( - (ent) => ent.platform === "script" && ent.unique_id === this.scriptId - ); - this.currentEntityId = entity?.entity_id; - this._checkValidation(); - }, - (resp) => { - const entity = this.entityRegistry.find( - (ent) => ent.platform === "script" && ent.unique_id === this.scriptId - ); - if (entity) { - navigate(`/config/script/show/${entity.entity_id}`, { - replace: true, - }); - return; - } - alert( - resp.status_code === 404 - ? this.hass.localize( - "ui.panel.config.script.editor.load_error_not_editable" - ) - : this.hass.localize( - "ui.panel.config.script.editor.load_error_unknown", - { err_no: resp.status_code || resp.code } - ) - ); - goBack("/config"); - } - ); - } - private _valueChanged(ev) { if (this.config) { this._undoRedoController.commit(this.config); diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index 68926f3193..71366faf5c 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -146,8 +146,6 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { @property({ attribute: false }) public cloudStatus?: CloudStatus; - @property({ attribute: false }) public entityRegistry!: EntityRegistryEntry[]; - @state() private _searchParms = new URLSearchParams(window.location.search); @state() private _selected: string[] = []; @@ -933,7 +931,7 @@ ${rejected } private _handleRowClicked(ev: HASSDomEvent) { - const entry = this.entityRegistry.find((e) => e.entity_id === ev.detail.id); + const entry = this._entityReg.find((e) => e.entity_id === ev.detail.id); if (entry) { navigate(`/config/script/edit/${entry.unique_id}`); } else { @@ -950,9 +948,7 @@ ${rejected } private _runScript = async (script: any) => { - const entry = this.entityRegistry.find( - (e) => e.entity_id === script.entity_id - ); + const entry = this._entityReg.find((e) => e.entity_id === script.entity_id); if (!entry) { return; } @@ -981,9 +977,7 @@ ${rejected } private _showTrace(script: any) { - const entry = this.entityRegistry.find( - (e) => e.entity_id === script.entity_id - ); + const entry = this._entityReg.find((e) => e.entity_id === script.entity_id); if (entry) { navigate(`/config/script/trace/${entry.unique_id}`); } @@ -1009,7 +1003,7 @@ ${rejected private async _duplicate(script: any) { try { - const entry = this.entityRegistry.find( + const entry = this._entityReg.find( (e) => e.entity_id === script.entity_id ); if (!entry) { @@ -1058,7 +1052,7 @@ ${rejected private async _delete(script: any) { try { - const entry = this.entityRegistry.find( + const entry = this._entityReg.find( (e) => e.entity_id === script.entity_id ); if (entry) { diff --git a/src/panels/config/script/ha-script-trace.ts b/src/panels/config/script/ha-script-trace.ts index a7c2df535c..ac75a60b9d 100644 --- a/src/panels/config/script/ha-script-trace.ts +++ b/src/panels/config/script/ha-script-trace.ts @@ -13,6 +13,7 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { repeat } from "lit/directives/repeat"; +import { consume } from "@lit/context"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time"; import { fireEvent } from "../../../common/dom/fire_event"; @@ -44,6 +45,7 @@ import { haStyle } from "../../../resources/styles"; import type { HomeAssistant, Route } from "../../../types"; import { fileDownload } from "../../../util/file_download"; import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown"; +import { fullEntitiesContext } from "../../../data/context"; @customElement("ha-script-trace") export class HaScriptTrace extends LitElement { @@ -59,7 +61,9 @@ export class HaScriptTrace extends LitElement { @property({ attribute: false }) public route!: Route; - @property({ attribute: false }) public entityRegistry!: EntityRegistryEntry[]; + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + _entityRegistry!: EntityRegistryEntry[]; @state() private _entityId?: string; @@ -346,7 +350,7 @@ export class HaScriptTrace extends LitElement { const params = new URLSearchParams(location.search); this._loadTraces(params.get("run_id") || undefined); - this._entityId = this.entityRegistry.find( + this._entityId = this._entityRegistry.find( (entry) => entry.unique_id === this.scriptId )?.entity_id; } @@ -363,7 +367,7 @@ export class HaScriptTrace extends LitElement { if (this.scriptId) { this._loadTraces(); - this._entityId = this.entityRegistry.find( + this._entityId = this._entityRegistry.find( (entry) => entry.unique_id === this.scriptId )?.entity_id; }