Change the area of scenes in editor (#10731)

This commit is contained in:
Paulus Schoutsen 2021-12-01 04:54:28 -08:00 committed by GitHub
parent ac8f748656
commit ceac9834b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 127 additions and 14 deletions

View File

@ -18,10 +18,15 @@ export const SCENE_IGNORED_DOMAINS = [
"zone", "zone",
]; ];
let inititialSceneEditorData: Partial<SceneConfig> | undefined; let inititialSceneEditorData:
| { config?: Partial<SceneConfig>; areaId?: string }
| undefined;
export const showSceneEditor = (data?: Partial<SceneConfig>) => { export const showSceneEditor = (
inititialSceneEditorData = data; config?: Partial<SceneConfig>,
areaId?: string
) => {
inititialSceneEditorData = { config, areaId };
navigate("/config/scene/edit/new"); navigate("/config/scene/edit/new");
}; };

View File

@ -32,6 +32,7 @@ import "../../../components/ha-card";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import "../../../components/ha-icon-picker"; import "../../../components/ha-icon-picker";
import "../../../components/ha-area-picker";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
import { import {
computeDeviceName, computeDeviceName,
@ -41,6 +42,7 @@ import {
import { import {
EntityRegistryEntry, EntityRegistryEntry,
subscribeEntityRegistry, subscribeEntityRegistry,
updateEntityRegistryEntry,
} from "../../../data/entity_registry"; } from "../../../data/entity_registry";
import { import {
activateScene, activateScene,
@ -121,6 +123,22 @@ export class HaSceneEditor extends SubscribeMixin(
private _activateContextId?: string; private _activateContextId?: string;
@state() private _saving = false;
// undefined means not set in this session
// null means picked nothing.
@state() private _updatedAreaId?: string | null;
// Callback to be called when scene is set.
private _scenesSet?: () => void;
private _getRegistryAreaId = memoizeOne(
(entries: EntityRegistryEntry[], entity_id: string) => {
const entry = entries.find((ent) => ent.entity_id === entity_id);
return entry ? entry.area_id : null;
}
);
private _getEntitiesDevices = memoizeOne( private _getEntitiesDevices = memoizeOne(
( (
entities: string[], entities: string[],
@ -287,6 +305,16 @@ export class HaSceneEditor extends SubscribeMixin(
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
> >
</ha-icon-picker> </ha-icon-picker>
<ha-area-picker
.hass=${this.hass}
.label=${this.hass.localize(
"ui.panel.config.scene.editor.area"
)}
.name=${"area"}
.value=${this._sceneAreaIdWithUpdates || ""}
@value-changed=${this._areaChanged}
>
</ha-area-picker>
</div> </div>
</ha-card> </ha-card>
</ha-config-section> </ha-config-section>
@ -444,8 +472,9 @@ export class HaSceneEditor extends SubscribeMixin(
slot="fab" slot="fab"
.label=${this.hass.localize("ui.panel.config.scene.editor.save")} .label=${this.hass.localize("ui.panel.config.scene.editor.save")}
extended extended
.disabled=${this._saving}
@click=${this._saveScene} @click=${this._saveScene}
class=${classMap({ dirty: this._dirty })} class=${classMap({ dirty: this._dirty, saving: this._saving })}
> >
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon> <ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
</ha-fab> </ha-fab>
@ -474,12 +503,15 @@ export class HaSceneEditor extends SubscribeMixin(
this._config = { this._config = {
name: this.hass.localize("ui.panel.config.scene.editor.default_name"), name: this.hass.localize("ui.panel.config.scene.editor.default_name"),
entities: {}, entities: {},
...initData, ...initData?.config,
}; };
this._initEntities(this._config); this._initEntities(this._config);
if (initData) { if (initData?.areaId) {
this._dirty = true; this._updatedAreaId = initData.areaId;
} }
this._dirty =
initData !== undefined &&
(initData.areaId !== undefined || initData.config !== undefined);
} }
if (changedProps.has("_entityRegistryEntries")) { if (changedProps.has("_entityRegistryEntries")) {
@ -514,6 +546,9 @@ export class HaSceneEditor extends SubscribeMixin(
) { ) {
this._setScene(); this._setScene();
} }
if (this._scenesSet && changedProps.has("scenes")) {
this._scenesSet();
}
} }
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) { private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
@ -689,6 +724,21 @@ export class HaSceneEditor extends SubscribeMixin(
this._dirty = true; this._dirty = true;
} }
private _areaChanged(ev: CustomEvent) {
const newValue = ev.detail.value === "" ? null : ev.detail.value;
if (newValue === (this._sceneAreaIdWithUpdates || "")) {
return;
}
if (newValue === this._sceneAreaIdCurrent) {
this._updatedAreaId = undefined;
} else {
this._updatedAreaId = newValue;
this._dirty = true;
}
}
private _stateChanged(event: HassEvent) { private _stateChanged(event: HassEvent) {
if ( if (
event.context.id !== this._activateContextId && event.context.id !== this._activateContextId &&
@ -749,13 +799,16 @@ export class HaSceneEditor extends SubscribeMixin(
// Wait for dialog to complete closing // Wait for dialog to complete closing
await new Promise((resolve) => setTimeout(resolve, 0)); await new Promise((resolve) => setTimeout(resolve, 0));
} }
showSceneEditor({ showSceneEditor(
{
...this._config, ...this._config,
id: undefined, id: undefined,
name: `${this._config?.name} (${this.hass.localize( name: `${this._config?.name} (${this.hass.localize(
"ui.panel.config.scene.picker.duplicate" "ui.panel.config.scene.picker.duplicate"
)})`, )})`,
}); },
this._sceneAreaIdCurrent || undefined
);
} }
private _calculateStates(): SceneEntities { private _calculateStates(): SceneEntities {
@ -792,7 +845,41 @@ export class HaSceneEditor extends SubscribeMixin(
const id = !this.sceneId ? "" + Date.now() : this.sceneId!; const id = !this.sceneId ? "" + Date.now() : this.sceneId!;
this._config = { ...this._config!, entities: this._calculateStates() }; this._config = { ...this._config!, entities: this._calculateStates() };
try { try {
this._saving = true;
await saveScene(this.hass, id, this._config); await saveScene(this.hass, id, this._config);
if (this._updatedAreaId !== undefined) {
let scene =
this._scene ||
this.scenes.find(
(entity: SceneEntity) => entity.attributes.id === id
);
if (!scene) {
try {
await new Promise<void>((resolve, reject) => {
setTimeout(reject, 3000);
this._scenesSet = resolve;
});
scene = this.scenes.find(
(entity: SceneEntity) => entity.attributes.id === id
);
} catch (err) {
// We do nothing.
} finally {
this._scenesSet = undefined;
}
}
if (scene) {
await updateEntityRegistryEntry(this.hass, scene.entity_id, {
area_id: this._updatedAreaId,
});
}
this._updatedAreaId = undefined;
}
this._dirty = false; this._dirty = false;
if (!this.sceneId) { if (!this.sceneId) {
@ -804,6 +891,8 @@ export class HaSceneEditor extends SubscribeMixin(
message: err.body.message || err.message, message: err.body.message || err.message,
}); });
throw err; throw err;
} finally {
this._saving = false;
} }
} }
@ -811,6 +900,21 @@ export class HaSceneEditor extends SubscribeMixin(
this._saveScene(); this._saveScene();
} }
private get _sceneAreaIdWithUpdates(): string | undefined | null {
return this._updatedAreaId !== undefined
? this._updatedAreaId
: this._sceneAreaIdCurrent;
}
private get _sceneAreaIdCurrent(): string | undefined | null {
return this._scene
? this._getRegistryAreaId(
this._entityRegistryEntries,
this._scene.entity_id
)
: undefined;
}
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [ return [
haStyle, haStyle,
@ -877,6 +981,9 @@ export class HaSceneEditor extends SubscribeMixin(
ha-fab.dirty { ha-fab.dirty {
bottom: 0; bottom: 0;
} }
ha-fab.saving {
opacity: var(--light-disabled-opacity);
}
`, `,
]; ];
} }

View File

@ -1900,6 +1900,7 @@
"unsaved_confirm": "You have unsaved changes. Are you sure you want to leave?", "unsaved_confirm": "You have unsaved changes. Are you sure you want to leave?",
"name": "Name", "name": "Name",
"icon": "Icon", "icon": "Icon",
"area": "Area",
"devices": { "devices": {
"header": "Devices", "header": "Devices",
"introduction": "Add the devices that you want to be included in your scene. Set all the devices to the state you want for this scene.", "introduction": "Add the devices that you want to be included in your scene. Set all the devices to the state you want for this scene.",