diff --git a/src/panels/config/scene/ha-config-scene.ts b/src/panels/config/scene/ha-config-scene.ts index e39f5da51e..1849969716 100644 --- a/src/panels/config/scene/ha-config-scene.ts +++ b/src/panels/config/scene/ha-config-scene.ts @@ -10,6 +10,14 @@ import { import { HomeAssistant } from "../../../types"; import "./ha-scene-dashboard"; import "./ha-scene-editor"; +import { debounce } from "../../../common/util/debounce"; + +const equal = (a: SceneEntity[], b: SceneEntity[]): boolean => { + if (a.length !== b.length) { + return false; + } + return a.every((scene, index) => scene === b[index]); +}; @customElement("ha-config-scene") class HaConfigScene extends HassRouterPage { @@ -36,15 +44,18 @@ class HaConfigScene extends HassRouterPage { }, }; - private _computeScenes = memoizeOne((states: HassEntities) => { - const scenes: SceneEntity[] = []; - Object.values(states).forEach((state) => { - if (computeStateDomain(state) === "scene" && !state.attributes.hidden) { - scenes.push(state as SceneEntity); - } - }); + private _debouncedUpdateScenes = debounce((pageEl) => { + const newScenes = this._getScenes(this.hass.states); + if (!equal(newScenes, pageEl.scenes)) { + pageEl.scenes = newScenes; + } + }, 10); - return scenes; + private _getScenes = memoizeOne((states: HassEntities): SceneEntity[] => { + return Object.values(states).filter( + (entity) => + computeStateDomain(entity) === "scene" && !entity.attributes.hidden + ) as SceneEntity[]; }); protected updatePageEl(pageEl, changedProps: PropertyValues) { @@ -55,7 +66,11 @@ class HaConfigScene extends HassRouterPage { pageEl.showAdvanced = this.showAdvanced; if (this.hass) { - pageEl.scenes = this._computeScenes(this.hass.states); + if (!pageEl.scenes || !changedProps) { + pageEl.scenes = this._getScenes(this.hass.states); + } else if (changedProps && changedProps.has("hass")) { + this._debouncedUpdateScenes(pageEl); + } } if ( @@ -64,13 +79,7 @@ class HaConfigScene extends HassRouterPage { ) { pageEl.creatingNew = undefined; const sceneId = this.routeTail.path.substr(1); - pageEl.creatingNew = sceneId === "new"; - pageEl.scene = - sceneId === "new" - ? undefined - : pageEl.scenes.find( - (entity: SceneEntity) => entity.attributes.id === sceneId - ); + pageEl.sceneId = sceneId === "new" ? null : sceneId; } } } diff --git a/src/panels/config/scene/ha-scene-editor.ts b/src/panels/config/scene/ha-scene-editor.ts index c9730784ef..1df1551139 100644 --- a/src/panels/config/scene/ha-scene-editor.ts +++ b/src/panels/config/scene/ha-scene-editor.ts @@ -46,7 +46,10 @@ import { SceneEntity, SCENE_IGNORED_DOMAINS, } from "../../../data/scene"; -import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; +import { + showConfirmationDialog, + showAlertDialog, +} from "../../../dialogs/generic/show-dialog-box"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; @@ -73,13 +76,13 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { @property() public route!: Route; - @property() public scene?: SceneEntity; + @property() public sceneId?: string; - @property() public creatingNew?: boolean; + @property() public scenes!: SceneEntity[]; @property() public showAdvanced!: boolean; - @property() private _dirty?: boolean; + @property() private _dirty = false; @property() private _errors?: string; @@ -93,6 +96,8 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { @property() private _entityRegistryEntries: EntityRegistryEntry[] = []; + @property() private _scene?: SceneEntity; + private _storedStates: SceneEntities = {}; private _unsubscribeEvents?: () => void; @@ -172,8 +177,8 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { this._deviceEntityLookup, this._deviceRegistryEntries ); - const name = this.scene - ? computeStateName(this.scene) + const name = this._scene + ? computeStateName(this._scene) : this.hass.localize("ui.panel.config.scene.editor.default_name"); return html` @@ -184,7 +189,7 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { .backCallback=${() => this._backTapped()} .tabs=${configSections.automation} > - ${this.creatingNew + ${!this.sceneId ? "" : html`
${device.entities.map((entityId) => { - const stateObj = this.hass.states[entityId]; - if (!stateObj) { + const entityStateObj = this.hass.states[entityId]; + if (!entityStateObj) { return html``; } return html` @@ -261,11 +266,11 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { class="device-entity" > - ${computeStateName(stateObj)} + ${computeStateName(entityStateObj)} `; @@ -313,8 +318,8 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { )} > ${entities.map((entityId) => { - const stateObj = this.hass.states[entityId]; - if (!stateObj) { + const entityStateObj = this.hass.states[entityId]; + if (!entityStateObj) { return html``; } return html` @@ -324,11 +329,11 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { class="device-entity" > - ${computeStateName(stateObj)} + ${computeStateName(entityStateObj)} entity.attributes.id === this.sceneId + ); + if (!scene) { + return; + } + this._scene = scene; + const { context } = await activateScene(this.hass, this._scene.entity_id); + this._activateContextId = context.id; + this._unsubscribeEvents = await this.hass!.connection.subscribeEvents< + HassEvent + >((event) => this._stateChanged(event), "state_changed"); } private _showMoreInfo(ev: Event) { @@ -446,20 +474,20 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { private async _loadConfig() { let config: SceneConfig; try { - config = await getSceneConfig(this.hass, this.scene!.attributes.id!); + config = await getSceneConfig(this.hass, this.sceneId!); } catch (err) { - alert( - err.status_code === 404 - ? this.hass.localize( - "ui.panel.config.scene.editor.load_error_not_editable" - ) - : this.hass.localize( - "ui.panel.config.scene.editor.load_error_unknown", - "err_no", - err.status_code - ) - ); - history.back(); + showAlertDialog(this, { + text: + err.status_code === 404 + ? this.hass.localize( + "ui.panel.config.scene.editor.load_error_not_editable" + ) + : this.hass.localize( + "ui.panel.config.scene.editor.load_error_unknown", + "err_no", + err.status_code + ), + }).then(() => history.back()); return; } @@ -469,13 +497,7 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { this._initEntities(config); - const { context } = await activateScene(this.hass, this.scene!.entity_id); - - this._activateContextId = context.id; - - this._unsubscribeEvents = await this.hass!.connection.subscribeEvents< - HassEvent - >((event) => this._stateChanged(event), "state_changed"); + this._setScene(); this._dirty = false; this._config = config; @@ -564,6 +586,7 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { event.context.id !== this._activateContextId && this._entities.includes(event.data.entity_id) ) { + console.log(event); this._dirty = true; } } @@ -598,7 +621,7 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { } private async _delete(): Promise { - await deleteScene(this.hass, this.scene!.attributes.id!); + await deleteScene(this.hass, this.sceneId!); applyScene(this.hass, this._storedStates); history.back(); } @@ -634,13 +657,13 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { } private async _saveScene(): Promise { - const id = this.creatingNew ? "" + Date.now() : this.scene!.attributes.id!; + const id = !this.sceneId ? "" + Date.now() : this.sceneId!; this._config = { ...this._config, entities: this._calculateStates() }; try { await saveScene(this.hass, id, this._config); this._dirty = false; - if (this.creatingNew) { + if (!this.sceneId) { navigate(this, `/config/scene/edit/${id}`, true); } } catch (err) {