mirror of
https://github.com/home-assistant/frontend.git
synced 2026-03-01 04:47:48 +00:00
Compare commits
6 Commits
dev
...
copilot/cr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8d8bbf5f9 | ||
|
|
be810d1f09 | ||
|
|
8e6f693c55 | ||
|
|
c9d35c0500 | ||
|
|
3b2a1ed5be | ||
|
|
1c50432dd4 |
@@ -27,19 +27,15 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { UndoRedoController } from "../../../common/controllers/undo-redo-controller";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { goBack, navigate } from "../../../common/navigate";
|
||||
import { promiseTimeout } from "../../../common/util/promise-timeout";
|
||||
import { afterNextRender } from "../../../common/util/render-status";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-fade-in";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-spinner";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-yaml-editor";
|
||||
import type {
|
||||
@@ -72,27 +68,22 @@ import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../dialogs/generic/show-dialog-box";
|
||||
import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog";
|
||||
import "../../../layouts/hass-subpage";
|
||||
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
||||
import { PreventUnsavedMixin } from "../../../mixins/prevent-unsaved-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type {
|
||||
Entries,
|
||||
HomeAssistant,
|
||||
Route,
|
||||
ValueChangedEvent,
|
||||
} from "../../../types";
|
||||
import type { Entries, ValueChangedEvent } from "../../../types";
|
||||
import { isMac } from "../../../util/is_mac";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||
import { showAutomationModeDialog } from "./automation-mode-dialog/show-dialog-automation-mode";
|
||||
import {
|
||||
type EntityRegistryUpdate,
|
||||
showAutomationSaveDialog,
|
||||
} from "./automation-save-dialog/show-dialog-automation-save";
|
||||
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 {
|
||||
AutomationScriptEditorMixin,
|
||||
automationScriptEditorStyles,
|
||||
} from "./ha-automation-script-editor-mixin";
|
||||
import "./manual-automation-editor";
|
||||
import type { HaManualAutomationEditor } from "./manual-automation-editor";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
@@ -119,53 +110,13 @@ declare global {
|
||||
}
|
||||
|
||||
@customElement("ha-automation-editor")
|
||||
export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
KeyboardShortcutMixin(LitElement)
|
||||
export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationConfig>(
|
||||
PreventUnsavedMixin(KeyboardShortcutMixin(LitElement))
|
||||
) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public automationId: string | null = null;
|
||||
|
||||
@property({ attribute: false }) public entityId: string | null = null;
|
||||
|
||||
@property({ attribute: false }) public automations!: AutomationEntity[];
|
||||
|
||||
@property({ attribute: "is-wide", type: Boolean }) public isWide = false;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@state() private _config?: AutomationConfig;
|
||||
|
||||
@state() private _dirty = false;
|
||||
|
||||
@state() private _errors?: string;
|
||||
|
||||
@state() private _yamlErrors?: string;
|
||||
|
||||
@state() private _entityId?: string;
|
||||
|
||||
@state() private _mode: "gui" | "yaml" = "gui";
|
||||
|
||||
@state() private _readOnly = false;
|
||||
|
||||
@state() private _validationErrors?: (string | TemplateResult)[];
|
||||
|
||||
@state() private _blueprintConfig?: BlueprintAutomationConfig;
|
||||
|
||||
@state()
|
||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||
@transform<EntityRegistryEntry[], EntityRegistryEntry>({
|
||||
transformer: function (this: HaAutomationEditor, value) {
|
||||
return value.find(({ entity_id }) => entity_id === this._entityId);
|
||||
},
|
||||
watch: ["_entityId"],
|
||||
})
|
||||
private _registryEntry?: EntityRegistryEntry;
|
||||
|
||||
@state() private _saving = false;
|
||||
|
||||
@state()
|
||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||
_entityRegistry!: EntityRegistryEntry[];
|
||||
@@ -180,24 +131,18 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
|
||||
private _configSubscriptionsId = 1;
|
||||
|
||||
private _entityRegistryUpdate?: EntityRegistryUpdate;
|
||||
|
||||
private _newAutomationId?: string;
|
||||
|
||||
private _entityRegCreated?: (
|
||||
value: PromiseLike<EntityRegistryEntry> | EntityRegistryEntry
|
||||
) => void;
|
||||
|
||||
private _undoRedoController = new UndoRedoController<AutomationConfig>(this, {
|
||||
apply: (config) => this._applyUndoRedo(config),
|
||||
currentConfig: () => this._config!,
|
||||
currentConfig: () => this.config!,
|
||||
});
|
||||
|
||||
protected willUpdate(changedProps) {
|
||||
super.willUpdate(changedProps);
|
||||
|
||||
if (
|
||||
this._entityRegCreated &&
|
||||
this.entityRegCreated &&
|
||||
this._newAutomationId &&
|
||||
changedProps.has("_entityRegistry")
|
||||
) {
|
||||
@@ -207,26 +152,22 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
entity.unique_id === this._newAutomationId
|
||||
);
|
||||
if (automation) {
|
||||
this._entityRegCreated(automation);
|
||||
this._entityRegCreated = undefined;
|
||||
this.entityRegCreated(automation);
|
||||
this.entityRegCreated = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | typeof nothing {
|
||||
if (!this._config) {
|
||||
return html`
|
||||
<ha-fade-in .delay=${500}>
|
||||
<ha-spinner size="large"></ha-spinner>
|
||||
</ha-fade-in>
|
||||
`;
|
||||
if (!this.config) {
|
||||
return this.renderLoading();
|
||||
}
|
||||
|
||||
const stateObj = this._entityId
|
||||
? this.hass.states[this._entityId]
|
||||
const stateObj = this.currentEntityId
|
||||
? this.hass.states[this.currentEntityId]
|
||||
: undefined;
|
||||
|
||||
const useBlueprint = "use_blueprint" in this._config;
|
||||
const useBlueprint = "use_blueprint" in this.config;
|
||||
const shortcutIcon = isMac
|
||||
? html`<ha-svg-icon .path=${mdiAppleKeyboardCommand}></ha-svg-icon>`
|
||||
: this.hass.localize("ui.panel.config.automation.editor.ctrl");
|
||||
@@ -236,11 +177,11 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.backCallback=${this._backTapped}
|
||||
.header=${this._config.alias ||
|
||||
.backCallback=${this.backTapped}
|
||||
.header=${this.config.alias ||
|
||||
this.hass.localize("ui.panel.config.automation.editor.default_name")}
|
||||
>
|
||||
${this._mode === "gui" && !this.narrow
|
||||
${this.mode === "gui" && !this.narrow
|
||||
? html`<ha-icon-button
|
||||
slot="toolbar-icon"
|
||||
.label=${this.hass.localize("ui.common.undo")}
|
||||
@@ -284,7 +225,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
</span>
|
||||
</ha-tooltip>`
|
||||
: nothing}
|
||||
${this._config?.id && !this.narrow
|
||||
${this.config?.id && !this.narrow
|
||||
? html`
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@@ -308,7 +249,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
|
||||
${this._mode === "gui" && this.narrow
|
||||
${this.mode === "gui" && this.narrow
|
||||
? html`<ha-dropdown-item
|
||||
value="undo"
|
||||
.disabled=${!this._undoRedoController.canUndo}
|
||||
@@ -342,7 +283,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
|
||||
<ha-dropdown-item .disabled=${!stateObj} value="category">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.scene.picker.${this._registryEntry?.categories?.automation ? "edit_category" : "assign_category"}`
|
||||
`ui.panel.config.scene.picker.${this.registryEntry?.categories?.automation ? "edit_category" : "assign_category"}`
|
||||
)}
|
||||
<ha-svg-icon slot="icon" .path=${mdiTag}></ha-svg-icon>
|
||||
</ha-dropdown-item>
|
||||
@@ -366,9 +307,9 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
|
||||
<ha-dropdown-item
|
||||
value="rename"
|
||||
.disabled=${this._readOnly ||
|
||||
.disabled=${this.readOnly ||
|
||||
!this.automationId ||
|
||||
this._mode === "yaml"}
|
||||
this.mode === "yaml"}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.rename")}
|
||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
||||
@@ -377,7 +318,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
? html`
|
||||
<ha-dropdown-item
|
||||
@click=${this._promptAutomationMode}
|
||||
.disabled=${this._readOnly || this._mode === "yaml"}
|
||||
.disabled=${this.readOnly || this.mode === "yaml"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.change_mode"
|
||||
@@ -391,12 +332,12 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
: nothing}
|
||||
|
||||
<ha-dropdown-item
|
||||
.disabled=${!!this._blueprintConfig ||
|
||||
(!this._readOnly && !this.automationId)}
|
||||
.disabled=${!!this.blueprintConfig ||
|
||||
(!this.readOnly && !this.automationId)}
|
||||
value="duplicate"
|
||||
>
|
||||
${this.hass.localize(
|
||||
this._readOnly
|
||||
this.readOnly
|
||||
? "ui.panel.config.automation.editor.migrate"
|
||||
: "ui.panel.config.automation.editor.duplicate"
|
||||
)}
|
||||
@@ -410,7 +351,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
? html`
|
||||
<ha-dropdown-item
|
||||
value="take_control"
|
||||
.disabled=${this._readOnly}
|
||||
.disabled=${this.readOnly}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.take_control"
|
||||
@@ -422,7 +363,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
|
||||
<ha-dropdown-item value="toggle_yaml_mode">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.edit_${this._mode === "gui" ? "yaml" : "ui"}`
|
||||
`ui.panel.config.automation.editor.edit_${this.mode === "gui" ? "yaml" : "ui"}`
|
||||
)}
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||
</ha-dropdown-item>
|
||||
@@ -456,10 +397,10 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
</ha-dropdown-item>
|
||||
</ha-dropdown>
|
||||
<div
|
||||
class=${this._mode === "yaml" ? "yaml-mode" : ""}
|
||||
class=${this.mode === "yaml" ? "yaml-mode" : ""}
|
||||
@subscribe-automation-config=${this._subscribeAutomationConfig}
|
||||
>
|
||||
${this._mode === "gui"
|
||||
${this.mode === "gui"
|
||||
? html`
|
||||
<div>
|
||||
${useBlueprint
|
||||
@@ -469,10 +410,10 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
.stateObj=${stateObj}
|
||||
.config=${this._config}
|
||||
.disabled=${this._readOnly}
|
||||
.saving=${this._saving}
|
||||
.dirty=${this._dirty}
|
||||
.config=${this.config}
|
||||
.disabled=${this.readOnly}
|
||||
.saving=${this.saving}
|
||||
.dirty=${this.dirty}
|
||||
@value-changed=${this._valueChanged}
|
||||
@save-automation=${this._handleSaveAutomation}
|
||||
></blueprint-automation-editor>
|
||||
@@ -483,16 +424,16 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
.stateObj=${stateObj}
|
||||
.config=${this._config}
|
||||
.disabled=${this._readOnly}
|
||||
.dirty=${this._dirty}
|
||||
.saving=${this._saving}
|
||||
.config=${this.config}
|
||||
.disabled=${this.readOnly}
|
||||
.dirty=${this.dirty}
|
||||
.saving=${this.saving}
|
||||
@value-changed=${this._valueChanged}
|
||||
@save-automation=${this._handleSaveAutomation}
|
||||
@editor-save=${this._handleSaveAutomation}
|
||||
>
|
||||
<div class="alert-wrapper" slot="alerts">
|
||||
${this._errors || stateObj?.state === UNAVAILABLE
|
||||
${this.errors || stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-alert
|
||||
alert-type="error"
|
||||
.title=${stateObj?.state === UNAVAILABLE
|
||||
@@ -501,7 +442,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
)
|
||||
: undefined}
|
||||
>
|
||||
${this._errors || this._validationErrors}
|
||||
${this.errors || this.validationErrors}
|
||||
${stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-svg-icon
|
||||
slot="icon"
|
||||
@@ -510,7 +451,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
: nothing}
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
${this._blueprintConfig
|
||||
${this.blueprintConfig
|
||||
? html`<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.confirm_take_control"
|
||||
@@ -518,21 +459,21 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
<div slot="action" style="display: flex;">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._takeControlSave}
|
||||
@click=${this.takeControlSave}
|
||||
>${this.hass.localize(
|
||||
"ui.common.yes"
|
||||
)}</ha-button
|
||||
>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._revertBlueprint}
|
||||
@click=${this.revertBlueprint}
|
||||
>${this.hass.localize(
|
||||
"ui.common.no"
|
||||
)}</ha-button
|
||||
>
|
||||
</div>
|
||||
</ha-alert>`
|
||||
: this._readOnly
|
||||
: this.readOnly
|
||||
? html`<ha-alert
|
||||
alert-type="warning"
|
||||
dismissable
|
||||
@@ -575,7 +516,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
`}
|
||||
</div>
|
||||
`
|
||||
: this._mode === "yaml"
|
||||
: this.mode === "yaml"
|
||||
? html`${stateObj?.state === "off"
|
||||
? html`
|
||||
<ha-alert alert-type="info">
|
||||
@@ -598,7 +539,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this._preprocessYaml()}
|
||||
.readOnly=${this._readOnly}
|
||||
.readOnly=${this.readOnly}
|
||||
@value-changed=${this._yamlChanged}
|
||||
@editor-save=${this._handleSaveAutomation}
|
||||
.showErrors=${false}
|
||||
@@ -606,9 +547,9 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
></ha-yaml-editor>
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
class=${this._dirty ? "dirty" : ""}
|
||||
class=${this.dirty ? "dirty" : ""}
|
||||
.label=${this.hass.localize("ui.common.save")}
|
||||
.disabled=${this._saving}
|
||||
.disabled=${this.saving}
|
||||
extended
|
||||
@click=${this._handleSaveAutomation}
|
||||
>
|
||||
@@ -645,7 +586,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
this.hass
|
||||
) {
|
||||
const initData = getAutomationEditorInitData();
|
||||
this._dirty = !!initData;
|
||||
this.dirty = !!initData;
|
||||
let baseConfig: Partial<AutomationConfig> = { description: "" };
|
||||
if (!initData || !("use_blueprint" in initData)) {
|
||||
baseConfig = {
|
||||
@@ -656,35 +597,35 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
actions: [],
|
||||
};
|
||||
}
|
||||
this._config = {
|
||||
this.config = {
|
||||
...baseConfig,
|
||||
...(initData ? normalizeAutomationConfig(initData) : initData),
|
||||
} as AutomationConfig;
|
||||
this._entityId = undefined;
|
||||
this._readOnly = false;
|
||||
this.currentEntityId = undefined;
|
||||
this.readOnly = false;
|
||||
}
|
||||
|
||||
if (changedProps.has("entityId") && this.entityId) {
|
||||
getAutomationStateConfig(this.hass, this.entityId).then((c) => {
|
||||
this._config = normalizeAutomationConfig(c.config);
|
||||
this.config = normalizeAutomationConfig(c.config);
|
||||
this._checkValidation();
|
||||
});
|
||||
this._entityId = this.entityId;
|
||||
this._dirty = false;
|
||||
this._readOnly = true;
|
||||
this.currentEntityId = this.entityId;
|
||||
this.dirty = false;
|
||||
this.readOnly = true;
|
||||
}
|
||||
|
||||
if (
|
||||
changedProps.has("automations") &&
|
||||
this.automationId &&
|
||||
!this._entityId
|
||||
!this.currentEntityId
|
||||
) {
|
||||
this._setEntityId();
|
||||
}
|
||||
|
||||
if (changedProps.has("_config")) {
|
||||
if (changedProps.has("config")) {
|
||||
Object.values(this._configSubscriptions).forEach((sub) =>
|
||||
sub(this._config)
|
||||
sub(this.config)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -693,24 +634,24 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
const automation = this.automations.find(
|
||||
(entity: AutomationEntity) => entity.attributes.id === this.automationId
|
||||
);
|
||||
this._entityId = automation?.entity_id;
|
||||
this.currentEntityId = automation?.entity_id;
|
||||
}
|
||||
|
||||
private async _checkValidation() {
|
||||
this._validationErrors = undefined;
|
||||
if (!this._entityId || !this._config) {
|
||||
this.validationErrors = undefined;
|
||||
if (!this.currentEntityId || !this.config) {
|
||||
return;
|
||||
}
|
||||
const stateObj = this.hass.states[this._entityId];
|
||||
const stateObj = this.hass.states[this.currentEntityId];
|
||||
if (stateObj?.state !== UNAVAILABLE) {
|
||||
return;
|
||||
}
|
||||
const validation = await validateConfig(this.hass, {
|
||||
triggers: this._config.triggers,
|
||||
conditions: this._config.conditions,
|
||||
actions: this._config.actions,
|
||||
triggers: this.config.triggers,
|
||||
conditions: this.config.conditions,
|
||||
actions: this.config.actions,
|
||||
});
|
||||
this._validationErrors = (
|
||||
this.validationErrors = (
|
||||
Object.entries(validation) as Entries<typeof validation>
|
||||
).map(([key, value]) =>
|
||||
value.valid
|
||||
@@ -728,9 +669,9 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
this.hass,
|
||||
this.automationId as string
|
||||
);
|
||||
this._dirty = false;
|
||||
this._readOnly = false;
|
||||
this._config = normalizeAutomationConfig(config);
|
||||
this.dirty = false;
|
||||
this.readOnly = false;
|
||||
this.config = normalizeAutomationConfig(config);
|
||||
this._checkValidation();
|
||||
} catch (err: any) {
|
||||
const entity = this._entityRegistry.find(
|
||||
@@ -761,34 +702,27 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
private _valueChanged(ev: ValueChangedEvent<AutomationConfig>) {
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this._config) {
|
||||
this._undoRedoController.commit(this._config);
|
||||
if (this.config) {
|
||||
this._undoRedoController.commit(this.config);
|
||||
}
|
||||
|
||||
this._config = ev.detail.value;
|
||||
if (this._readOnly) {
|
||||
this.config = ev.detail.value;
|
||||
if (this.readOnly) {
|
||||
return;
|
||||
}
|
||||
this._dirty = true;
|
||||
this._errors = undefined;
|
||||
this.dirty = true;
|
||||
this.errors = undefined;
|
||||
}
|
||||
|
||||
private _showInfo() {
|
||||
if (!this.hass || !this._entityId) {
|
||||
if (!this.hass || !this.currentEntityId) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "hass-more-info", { entityId: this._entityId });
|
||||
}
|
||||
|
||||
private _showSettings() {
|
||||
showMoreInfoDialog(this, {
|
||||
entityId: this._entityId!,
|
||||
view: "settings",
|
||||
});
|
||||
fireEvent(this, "hass-more-info", { entityId: this.currentEntityId });
|
||||
}
|
||||
|
||||
private _editCategory() {
|
||||
if (!this._registryEntry) {
|
||||
if (!this.registryEntry) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.scene.picker.no_category_support"
|
||||
@@ -801,36 +735,36 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
}
|
||||
showAssignCategoryDialog(this, {
|
||||
scope: "automation",
|
||||
entityReg: this._registryEntry,
|
||||
entityReg: this.registryEntry,
|
||||
});
|
||||
}
|
||||
|
||||
private async _showTrace() {
|
||||
if (this._config?.id) {
|
||||
const result = await this._confirmUnsavedChanged();
|
||||
if (this.config?.id) {
|
||||
const result = await this.confirmUnsavedChanged();
|
||||
if (result) {
|
||||
navigate(
|
||||
`/config/automation/trace/${encodeURIComponent(this._config.id)}`
|
||||
`/config/automation/trace/${encodeURIComponent(this.config.id)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _runActions() {
|
||||
if (!this.hass || !this._entityId) {
|
||||
if (!this.hass || !this.currentEntityId) {
|
||||
return;
|
||||
}
|
||||
triggerAutomationActions(
|
||||
this.hass,
|
||||
this.hass.states[this._entityId].entity_id
|
||||
this.hass.states[this.currentEntityId].entity_id
|
||||
);
|
||||
}
|
||||
|
||||
private async _toggle(): Promise<void> {
|
||||
if (!this.hass || !this._entityId) {
|
||||
if (!this.hass || !this.currentEntityId) {
|
||||
return;
|
||||
}
|
||||
const stateObj = this.hass.states[this._entityId];
|
||||
const stateObj = this.hass.states[this.currentEntityId];
|
||||
const service = stateObj.state === "off" ? "turn_on" : "turn_off";
|
||||
await this.hass.callService("automation", service, {
|
||||
entity_id: stateObj.entity_id,
|
||||
@@ -838,42 +772,42 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
}
|
||||
|
||||
private _preprocessYaml() {
|
||||
if (!this._config) {
|
||||
if (!this.config) {
|
||||
return {};
|
||||
}
|
||||
const cleanConfig: AutomationConfig = { ...this._config };
|
||||
const cleanConfig: AutomationConfig = { ...this.config };
|
||||
delete cleanConfig.id;
|
||||
return cleanConfig;
|
||||
}
|
||||
|
||||
private _yamlChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
this._dirty = true;
|
||||
this.dirty = true;
|
||||
if (!ev.detail.isValid) {
|
||||
this._yamlErrors = ev.detail.errorMsg;
|
||||
this.yamlErrors = ev.detail.errorMsg;
|
||||
return;
|
||||
}
|
||||
this._yamlErrors = undefined;
|
||||
this._config = {
|
||||
id: this._config?.id,
|
||||
this.yamlErrors = undefined;
|
||||
this.config = {
|
||||
id: this.config?.id,
|
||||
...normalizeAutomationConfig(ev.detail.value),
|
||||
};
|
||||
this._errors = undefined;
|
||||
this.errors = undefined;
|
||||
}
|
||||
|
||||
private async _confirmUnsavedChanged(): Promise<boolean> {
|
||||
if (!this._dirty) {
|
||||
protected async confirmUnsavedChanged(): Promise<boolean> {
|
||||
if (!this.dirty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return new Promise<boolean>((resolve) => {
|
||||
showAutomationSaveDialog(this, {
|
||||
config: this._config!,
|
||||
config: this.config!,
|
||||
domain: "automation",
|
||||
updateConfig: async (config, entityRegistryUpdate) => {
|
||||
this._config = config;
|
||||
this._entityRegistryUpdate = entityRegistryUpdate;
|
||||
this._dirty = true;
|
||||
this.config = config;
|
||||
this.entityRegistryUpdate = entityRegistryUpdate;
|
||||
this.dirty = true;
|
||||
this.requestUpdate();
|
||||
|
||||
const id = this.automationId || String(Date.now());
|
||||
@@ -889,8 +823,8 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
},
|
||||
onClose: () => resolve(false),
|
||||
onDiscard: () => resolve(true),
|
||||
entityRegistryUpdate: this._entityRegistryUpdate,
|
||||
entityRegistryEntry: this._registryEntry,
|
||||
entityRegistryUpdate: this.entityRegistryUpdate,
|
||||
entityRegistryEntry: this.registryEntry,
|
||||
title: this.hass.localize(
|
||||
this.automationId
|
||||
? "ui.panel.config.automation.editor.leave.unsaved_confirm_title"
|
||||
@@ -906,15 +840,8 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
});
|
||||
}
|
||||
|
||||
private _backTapped = async () => {
|
||||
const result = await this._confirmUnsavedChanged();
|
||||
if (result) {
|
||||
afterNextRender(() => goBack("/config"));
|
||||
}
|
||||
};
|
||||
|
||||
private async _takeControl() {
|
||||
const config = this._config as BlueprintAutomationConfig;
|
||||
const config = this.config as BlueprintAutomationConfig;
|
||||
|
||||
try {
|
||||
const result = await substituteBlueprint(
|
||||
@@ -931,35 +858,20 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
description: config.description,
|
||||
};
|
||||
|
||||
this._blueprintConfig = config;
|
||||
this._config = newConfig;
|
||||
if (this._mode === "yaml") {
|
||||
this.renderRoot.querySelector("ha-yaml-editor")?.setValue(this._config);
|
||||
this.blueprintConfig = config;
|
||||
this.config = newConfig;
|
||||
if (this.mode === "yaml") {
|
||||
this.renderRoot.querySelector("ha-yaml-editor")?.setValue(this.config);
|
||||
}
|
||||
this._readOnly = true;
|
||||
this._errors = undefined;
|
||||
this.readOnly = true;
|
||||
this.errors = undefined;
|
||||
} catch (err: any) {
|
||||
this._errors = err.message;
|
||||
this.errors = err.message;
|
||||
}
|
||||
}
|
||||
|
||||
private _revertBlueprint() {
|
||||
this._config = this._blueprintConfig;
|
||||
if (this._mode === "yaml") {
|
||||
this.renderRoot.querySelector("ha-yaml-editor")?.setValue(this._config);
|
||||
}
|
||||
this._blueprintConfig = undefined;
|
||||
this._readOnly = false;
|
||||
}
|
||||
|
||||
private _takeControlSave() {
|
||||
this._readOnly = false;
|
||||
this._dirty = true;
|
||||
this._blueprintConfig = undefined;
|
||||
}
|
||||
|
||||
private async _duplicate() {
|
||||
const result = this._readOnly
|
||||
const result = this.readOnly
|
||||
? await showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.automation.picker.migrate_automation"
|
||||
@@ -968,12 +880,12 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
"ui.panel.config.automation.picker.migrate_automation_description"
|
||||
),
|
||||
})
|
||||
: await this._confirmUnsavedChanged();
|
||||
: await this.confirmUnsavedChanged();
|
||||
if (result) {
|
||||
showAutomationEditor({
|
||||
...this._config,
|
||||
...this.config,
|
||||
id: undefined,
|
||||
alias: this._readOnly ? this._config?.alias : undefined,
|
||||
alias: this.readOnly ? this.config?.alias : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -985,7 +897,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.automation.picker.delete_confirm_text",
|
||||
{ name: this._config?.alias }
|
||||
{ name: this.config?.alias }
|
||||
),
|
||||
confirmText: this.hass!.localize("ui.common.delete"),
|
||||
destructive: true,
|
||||
@@ -1001,43 +913,21 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
}
|
||||
}
|
||||
|
||||
private async _switchUiMode() {
|
||||
if (this._yamlErrors) {
|
||||
const result = await showConfirmationDialog(this, {
|
||||
text: html`${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.switch_ui_yaml_error"
|
||||
)}<br /><br />${this._yamlErrors}`,
|
||||
confirmText: this.hass!.localize("ui.common.continue"),
|
||||
destructive: true,
|
||||
dismissText: this.hass!.localize("ui.common.cancel"),
|
||||
});
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._yamlErrors = undefined;
|
||||
this._mode = "gui";
|
||||
}
|
||||
|
||||
private _switchYamlMode() {
|
||||
this._mode = "yaml";
|
||||
}
|
||||
|
||||
private async _promptAutomationAlias(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
showAutomationSaveDialog(this, {
|
||||
config: this._config!,
|
||||
config: this.config!,
|
||||
domain: "automation",
|
||||
updateConfig: async (config, entityRegistryUpdate) => {
|
||||
this._config = config;
|
||||
this._entityRegistryUpdate = entityRegistryUpdate;
|
||||
this._dirty = true;
|
||||
this.config = config;
|
||||
this.entityRegistryUpdate = entityRegistryUpdate;
|
||||
this.dirty = true;
|
||||
this.requestUpdate();
|
||||
resolve(true);
|
||||
},
|
||||
onClose: () => resolve(false),
|
||||
entityRegistryUpdate: this._entityRegistryUpdate,
|
||||
entityRegistryEntry: this._registryEntry,
|
||||
entityRegistryUpdate: this.entityRegistryUpdate,
|
||||
entityRegistryEntry: this.registryEntry,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1045,10 +935,10 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
private async _promptAutomationMode(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
showAutomationModeDialog(this, {
|
||||
config: this._config!,
|
||||
config: this.config!,
|
||||
updateConfig: (config) => {
|
||||
this._config = config;
|
||||
this._dirty = true;
|
||||
this.config = config;
|
||||
this.dirty = true;
|
||||
this.requestUpdate();
|
||||
resolve();
|
||||
},
|
||||
@@ -1058,9 +948,9 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
}
|
||||
|
||||
private async _handleSaveAutomation(): Promise<void> {
|
||||
if (this._yamlErrors) {
|
||||
if (this.yamlErrors) {
|
||||
showToast(this, {
|
||||
message: this._yamlErrors,
|
||||
message: this.yamlErrors,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -1082,22 +972,22 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
}
|
||||
|
||||
private async _saveAutomation(id): Promise<void> {
|
||||
this._saving = true;
|
||||
this._validationErrors = undefined;
|
||||
this.saving = true;
|
||||
this.validationErrors = undefined;
|
||||
|
||||
let entityRegPromise: Promise<EntityRegistryEntry> | undefined;
|
||||
if (this._entityRegistryUpdate !== undefined && !this._entityId) {
|
||||
if (this.entityRegistryUpdate !== undefined && !this.currentEntityId) {
|
||||
this._newAutomationId = id;
|
||||
entityRegPromise = new Promise<EntityRegistryEntry>((resolve) => {
|
||||
this._entityRegCreated = resolve;
|
||||
this.entityRegCreated = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await saveAutomationConfig(this.hass, id, this._config!);
|
||||
await saveAutomationConfig(this.hass, id, this.config!);
|
||||
|
||||
if (this._entityRegistryUpdate !== undefined) {
|
||||
let entityId = this._entityId;
|
||||
if (this.entityRegistryUpdate !== undefined) {
|
||||
let entityId = this.currentEntityId;
|
||||
|
||||
// wait for automation to appear in entity registry when creating a new automation
|
||||
if (entityRegPromise) {
|
||||
@@ -1131,23 +1021,23 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
if (entityId) {
|
||||
await updateEntityRegistryEntry(this.hass, entityId, {
|
||||
categories: {
|
||||
automation: this._entityRegistryUpdate.category || null,
|
||||
automation: this.entityRegistryUpdate.category || null,
|
||||
},
|
||||
labels: this._entityRegistryUpdate.labels || [],
|
||||
area_id: this._entityRegistryUpdate.area || null,
|
||||
labels: this.entityRegistryUpdate.labels || [],
|
||||
area_id: this.entityRegistryUpdate.area || null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._dirty = false;
|
||||
this.dirty = false;
|
||||
} catch (errors: any) {
|
||||
this._errors = errors.body?.message || errors.error || errors.body;
|
||||
this.errors = errors.body?.message || errors.error || errors.body;
|
||||
showToast(this, {
|
||||
message: errors.body?.message || errors.error || errors.body,
|
||||
});
|
||||
throw errors;
|
||||
} finally {
|
||||
this._saving = false;
|
||||
this.saving = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1157,7 +1047,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
ev.detail.unsub = () => {
|
||||
delete this._configSubscriptions[id];
|
||||
};
|
||||
ev.detail.callback(this._config);
|
||||
ev.detail.callback(this.config);
|
||||
}
|
||||
|
||||
protected supportedShortcuts(): SupportedShortcuts {
|
||||
@@ -1173,14 +1063,6 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
};
|
||||
}
|
||||
|
||||
protected get isDirty() {
|
||||
return this._dirty;
|
||||
}
|
||||
|
||||
protected async promptDiscardChanges() {
|
||||
return this._confirmUnsavedChanged();
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
private _collapseAll() {
|
||||
this._manualEditor?.collapseAll();
|
||||
@@ -1205,8 +1087,8 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
|
||||
private _applyUndoRedo(config: AutomationConfig) {
|
||||
this._manualEditor?.triggerCloseSidebar();
|
||||
this._config = config;
|
||||
this._dirty = true;
|
||||
this.config = config;
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
private _undo() {
|
||||
@@ -1235,7 +1117,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
this._showInfo();
|
||||
break;
|
||||
case "settings":
|
||||
this._showSettings();
|
||||
this.showSettings();
|
||||
break;
|
||||
case "category":
|
||||
this._editCategory();
|
||||
@@ -1256,11 +1138,11 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
this._takeControl();
|
||||
break;
|
||||
case "toggle_yaml_mode":
|
||||
if (this._mode === "gui") {
|
||||
this._switchYamlMode();
|
||||
if (this.mode === "gui") {
|
||||
this.switchYamlMode();
|
||||
break;
|
||||
}
|
||||
this._switchUiMode();
|
||||
this.switchUiMode();
|
||||
break;
|
||||
case "disable":
|
||||
this._toggle();
|
||||
@@ -1277,25 +1159,8 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
automationScriptEditorStyles,
|
||||
css`
|
||||
:host {
|
||||
--ha-automation-editor-max-width: var(
|
||||
--ha-automation-editor-width,
|
||||
1540px
|
||||
);
|
||||
}
|
||||
ha-fade-in {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
.yaml-mode {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
manual-automation-editor,
|
||||
blueprint-automation-editor {
|
||||
margin: 0 auto;
|
||||
@@ -1309,17 +1174,6 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
ha-yaml-editor {
|
||||
flex-grow: 1;
|
||||
--actions-border-radius: var(--ha-border-radius-square);
|
||||
--code-mirror-height: 100%;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ha-entity-toggle {
|
||||
margin-right: 8px;
|
||||
margin-inline-end: 8px;
|
||||
@@ -1335,24 +1189,6 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
max-width: 1040px;
|
||||
padding: 28px 20px 0;
|
||||
}
|
||||
ha-fab {
|
||||
position: fixed;
|
||||
right: calc(16px + var(--safe-area-inset-right, 0px));
|
||||
bottom: calc(-80px - var(--safe-area-inset-bottom));
|
||||
transition: bottom 0.3s;
|
||||
}
|
||||
ha-fab.dirty {
|
||||
bottom: calc(16px + var(--safe-area-inset-bottom, 0px));
|
||||
}
|
||||
ha-tooltip ha-svg-icon {
|
||||
width: 12px;
|
||||
}
|
||||
ha-tooltip .shortcut {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
import { consume } from "@lit/context";
|
||||
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 { 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 { 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";
|
||||
import "../../../components/ha-fade-in";
|
||||
import "../../../components/ha-spinner"; // used by renderLoading() provided to both editors
|
||||
|
||||
/** Minimum config shape shared by both AutomationConfig and ScriptConfig. */
|
||||
interface BaseEditorConfig {
|
||||
alias?: string;
|
||||
}
|
||||
|
||||
/** Shared CSS styles for both automation and script editors. */
|
||||
export const automationScriptEditorStyles: CSSResult = css`
|
||||
:host {
|
||||
--ha-automation-editor-max-width: var(--ha-automation-editor-width, 1540px);
|
||||
}
|
||||
ha-fade-in {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
.yaml-mode {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
ha-yaml-editor {
|
||||
flex-grow: 1;
|
||||
--actions-border-radius: var(--ha-border-radius-square);
|
||||
--code-mirror-height: 100%;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ha-fab {
|
||||
position: fixed;
|
||||
right: calc(16px + var(--safe-area-inset-right, 0px));
|
||||
bottom: calc(-80px - var(--safe-area-inset-bottom));
|
||||
transition: bottom 0.3s;
|
||||
}
|
||||
ha-fab.dirty {
|
||||
bottom: calc(16px + var(--safe-area-inset-bottom, 0px));
|
||||
}
|
||||
ha-tooltip ha-svg-icon {
|
||||
width: 12px;
|
||||
}
|
||||
ha-tooltip .shortcut {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const AutomationScriptEditorMixin = <TConfig extends BaseEditorConfig>(
|
||||
superClass: Constructor<LitElement>
|
||||
) => {
|
||||
class AutomationScriptEditorClass extends superClass {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: "is-wide", type: Boolean }) public isWide = false;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@property({ attribute: false }) public entityId: string | null = null;
|
||||
|
||||
@state() protected dirty = false;
|
||||
|
||||
@state() protected errors?: string;
|
||||
|
||||
@state() protected yamlErrors?: string;
|
||||
|
||||
@state() protected currentEntityId?: string;
|
||||
|
||||
@state() protected mode: "gui" | "yaml" = "gui";
|
||||
|
||||
@state() protected readOnly = false;
|
||||
|
||||
@state() protected saving = false;
|
||||
|
||||
@state() protected validationErrors?: (string | TemplateResult)[];
|
||||
|
||||
@state() protected config?: TConfig;
|
||||
|
||||
@state() protected blueprintConfig?: TConfig;
|
||||
|
||||
@state()
|
||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||
@transform<EntityRegistryEntry[], EntityRegistryEntry>({
|
||||
transformer: function (this: { currentEntityId?: string }, value) {
|
||||
return value.find(
|
||||
({ entity_id }) => entity_id === this.currentEntityId
|
||||
);
|
||||
},
|
||||
watch: ["currentEntityId"],
|
||||
})
|
||||
protected registryEntry?: EntityRegistryEntry;
|
||||
|
||||
protected entityRegistryUpdate?: EntityRegistryUpdate;
|
||||
|
||||
protected entityRegCreated?: (
|
||||
value: PromiseLike<EntityRegistryEntry> | EntityRegistryEntry
|
||||
) => void;
|
||||
|
||||
protected renderLoading(): TemplateResult {
|
||||
return html`
|
||||
<ha-fade-in .delay=${500}>
|
||||
<ha-spinner size="large"></ha-spinner>
|
||||
</ha-fade-in>
|
||||
`;
|
||||
}
|
||||
|
||||
protected showSettings() {
|
||||
showMoreInfoDialog(this, {
|
||||
entityId: this.currentEntityId!,
|
||||
view: "settings",
|
||||
});
|
||||
}
|
||||
|
||||
protected async switchUiMode() {
|
||||
if (this.yamlErrors) {
|
||||
const result = await showConfirmationDialog(this, {
|
||||
text: html`${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.switch_ui_yaml_error"
|
||||
)}<br /><br />${this.yamlErrors}`,
|
||||
confirmText: this.hass!.localize("ui.common.continue"),
|
||||
destructive: true,
|
||||
dismissText: this.hass!.localize("ui.common.cancel"),
|
||||
});
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.yamlErrors = undefined;
|
||||
this.mode = "gui";
|
||||
}
|
||||
|
||||
protected switchYamlMode() {
|
||||
this.mode = "yaml";
|
||||
}
|
||||
|
||||
protected takeControlSave() {
|
||||
this.readOnly = false;
|
||||
this.dirty = true;
|
||||
this.blueprintConfig = undefined;
|
||||
}
|
||||
|
||||
protected revertBlueprint() {
|
||||
this.config = this.blueprintConfig;
|
||||
if (this.mode === "yaml") {
|
||||
this.renderRoot.querySelector("ha-yaml-editor")?.setValue(this.config);
|
||||
}
|
||||
this.blueprintConfig = undefined;
|
||||
this.readOnly = false;
|
||||
}
|
||||
|
||||
protected backTapped = async () => {
|
||||
const result = await this.confirmUnsavedChanged();
|
||||
if (result) {
|
||||
afterNextRender(() => goBack("/config"));
|
||||
}
|
||||
};
|
||||
|
||||
protected get isDirty() {
|
||||
return this.dirty;
|
||||
}
|
||||
|
||||
protected async promptDiscardChanges() {
|
||||
return this.confirmUnsavedChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks whether unsaved changes should be discarded.
|
||||
* Subclasses must override this to show a confirmation dialog.
|
||||
* @returns true to proceed (discard/save changes), false to cancel.
|
||||
*/
|
||||
protected confirmUnsavedChanged(): Promise<boolean> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
return AutomationScriptEditorClass;
|
||||
};
|
||||
@@ -1,5 +1,4 @@
|
||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
||||
import { consume } from "@lit/context";
|
||||
import {
|
||||
mdiAppleKeyboardCommand,
|
||||
mdiCog,
|
||||
@@ -22,15 +21,13 @@ import {
|
||||
} from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, css, html, 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 { transform } from "../../../common/decorators/transform";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { goBack, navigate } from "../../../common/navigate";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import { promiseTimeout } from "../../../common/util/promise-timeout";
|
||||
import { afterNextRender } from "../../../common/util/render-status";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
@@ -40,7 +37,6 @@ import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-yaml-editor";
|
||||
import { substituteBlueprint } from "../../../data/blueprint";
|
||||
import { validateConfig } from "../../../data/config";
|
||||
import { fullEntitiesContext } from "../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import {
|
||||
type EntityRegistryEntry,
|
||||
@@ -67,88 +63,47 @@ import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
||||
import { PreventUnsavedMixin } from "../../../mixins/prevent-unsaved-mixin";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { Entries, HomeAssistant, Route } from "../../../types";
|
||||
import type { Entries } from "../../../types";
|
||||
import { isMac } from "../../../util/is_mac";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { showAutomationModeDialog } from "../automation/automation-mode-dialog/show-dialog-automation-mode";
|
||||
import type { EntityRegistryUpdate } from "../automation/automation-save-dialog/show-dialog-automation-save";
|
||||
import { showAutomationSaveDialog } from "../automation/automation-save-dialog/show-dialog-automation-save";
|
||||
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 {
|
||||
AutomationScriptEditorMixin,
|
||||
automationScriptEditorStyles,
|
||||
} from "../automation/ha-automation-script-editor-mixin";
|
||||
import "./manual-script-editor";
|
||||
import type { HaManualScriptEditor } from "./manual-script-editor";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
|
||||
@customElement("ha-script-editor")
|
||||
export class HaScriptEditor extends SubscribeMixin(
|
||||
PreventUnsavedMixin(KeyboardShortcutMixin(LitElement))
|
||||
AutomationScriptEditorMixin<ScriptConfig>(
|
||||
PreventUnsavedMixin(KeyboardShortcutMixin(LitElement))
|
||||
)
|
||||
) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public scriptId: string | null = null;
|
||||
|
||||
@property({ attribute: false }) public entityId: string | null = null;
|
||||
|
||||
@property({ attribute: false }) public entityRegistry!: EntityRegistryEntry[];
|
||||
|
||||
@property({ attribute: "is-wide", type: Boolean }) public isWide = false;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@state() private _config?: ScriptConfig;
|
||||
|
||||
@state() private _dirty = false;
|
||||
|
||||
@state() private _errors?: string;
|
||||
|
||||
@state() private _yamlErrors?: string;
|
||||
|
||||
@state() private _entityId?: string;
|
||||
|
||||
@state() private _mode: "gui" | "yaml" = "gui";
|
||||
|
||||
@state() private _readOnly = false;
|
||||
|
||||
@state()
|
||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||
@transform<EntityRegistryEntry[], EntityRegistryEntry>({
|
||||
transformer: function (this: HaScriptEditor, value) {
|
||||
return value.find(({ entity_id }) => entity_id === this._entityId);
|
||||
},
|
||||
watch: ["_entityId"],
|
||||
})
|
||||
private _registryEntry?: EntityRegistryEntry;
|
||||
|
||||
@query("manual-script-editor")
|
||||
private _manualEditor?: HaManualScriptEditor;
|
||||
|
||||
@state() private _validationErrors?: (string | TemplateResult)[];
|
||||
|
||||
@state() private _blueprintConfig?: BlueprintScriptConfig;
|
||||
|
||||
@state() private _saving = false;
|
||||
|
||||
private _entityRegistryUpdate?: EntityRegistryUpdate;
|
||||
|
||||
private _newScriptId?: string;
|
||||
|
||||
private _entityRegCreated?: (
|
||||
value: PromiseLike<EntityRegistryEntry> | EntityRegistryEntry
|
||||
) => void;
|
||||
|
||||
private _undoRedoController = new UndoRedoController<ScriptConfig>(this, {
|
||||
apply: (config) => this._applyUndoRedo(config),
|
||||
currentConfig: () => this._config!,
|
||||
currentConfig: () => this.config!,
|
||||
});
|
||||
|
||||
protected willUpdate(changedProps) {
|
||||
super.willUpdate(changedProps);
|
||||
|
||||
if (
|
||||
this._entityRegCreated &&
|
||||
this.entityRegCreated &&
|
||||
this._newScriptId &&
|
||||
changedProps.has("entityRegistry")
|
||||
) {
|
||||
@@ -157,22 +112,22 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
entity.platform === "script" && entity.unique_id === this._newScriptId
|
||||
);
|
||||
if (script) {
|
||||
this._entityRegCreated(script);
|
||||
this._entityRegCreated = undefined;
|
||||
this.entityRegCreated(script);
|
||||
this.entityRegCreated = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | typeof nothing {
|
||||
if (!this._config) {
|
||||
return nothing;
|
||||
if (!this.config) {
|
||||
return this.renderLoading();
|
||||
}
|
||||
|
||||
const stateObj = this._entityId
|
||||
? this.hass.states[this._entityId]
|
||||
const stateObj = this.currentEntityId
|
||||
? this.hass.states[this.currentEntityId]
|
||||
: undefined;
|
||||
|
||||
const useBlueprint = "use_blueprint" in this._config;
|
||||
const useBlueprint = "use_blueprint" in this.config;
|
||||
const shortcutIcon = isMac
|
||||
? html`<ha-svg-icon .path=${mdiAppleKeyboardCommand}></ha-svg-icon>`
|
||||
: this.hass.localize("ui.panel.config.automation.editor.ctrl");
|
||||
@@ -182,11 +137,11 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.backCallback=${this._backTapped}
|
||||
.header=${this._config.alias ||
|
||||
.backCallback=${this.backTapped}
|
||||
.header=${this.config.alias ||
|
||||
this.hass.localize("ui.panel.config.script.editor.default_name")}
|
||||
>
|
||||
${this._mode === "gui" && !this.narrow
|
||||
${this.mode === "gui" && !this.narrow
|
||||
? html`<ha-icon-button
|
||||
slot="toolbar-icon"
|
||||
.label=${this.hass.localize("ui.common.undo")}
|
||||
@@ -252,7 +207,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
|
||||
${this._mode === "gui" && this.narrow
|
||||
${this.mode === "gui" && this.narrow
|
||||
? html`<ha-dropdown-item
|
||||
value="undo"
|
||||
.disabled=${!this._undoRedoController.canUndo}
|
||||
@@ -286,7 +241,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
|
||||
<ha-dropdown-item .disabled=${!stateObj} value="category">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.scene.picker.${this._registryEntry?.categories?.script ? "edit_category" : "assign_category"}`
|
||||
`ui.panel.config.scene.picker.${this.registryEntry?.categories?.script ? "edit_category" : "assign_category"}`
|
||||
)}
|
||||
<ha-svg-icon slot="icon" .path=${mdiTag}></ha-svg-icon>
|
||||
</ha-dropdown-item>
|
||||
@@ -307,10 +262,10 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
></ha-svg-icon>
|
||||
</ha-dropdown-item>`
|
||||
: nothing}
|
||||
${!useBlueprint && !("fields" in this._config)
|
||||
${!useBlueprint && !("fields" in this.config)
|
||||
? html`
|
||||
<ha-dropdown-item
|
||||
.disabled=${this._readOnly || this._mode === "yaml"}
|
||||
.disabled=${this.readOnly || this.mode === "yaml"}
|
||||
value="add_fields"
|
||||
>
|
||||
${this.hass.localize(
|
||||
@@ -326,9 +281,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
|
||||
<ha-dropdown-item
|
||||
value="rename"
|
||||
.disabled=${!this.scriptId ||
|
||||
this._readOnly ||
|
||||
this._mode === "yaml"}
|
||||
.disabled=${!this.scriptId || this.readOnly || this.mode === "yaml"}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.script.editor.rename")}
|
||||
<ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
|
||||
@@ -337,7 +290,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
? html`
|
||||
<ha-dropdown-item
|
||||
value="change_mode"
|
||||
.disabled=${this._readOnly || this._mode === "yaml"}
|
||||
.disabled=${this.readOnly || this.mode === "yaml"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.change_mode"
|
||||
@@ -351,12 +304,12 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
: nothing}
|
||||
|
||||
<ha-dropdown-item
|
||||
.disabled=${!!this._blueprintConfig ||
|
||||
(!this._readOnly && !this.scriptId)}
|
||||
.disabled=${!!this.blueprintConfig ||
|
||||
(!this.readOnly && !this.scriptId)}
|
||||
value="duplicate"
|
||||
>
|
||||
${this.hass.localize(
|
||||
this._readOnly
|
||||
this.readOnly
|
||||
? "ui.panel.config.script.editor.migrate"
|
||||
: "ui.panel.config.script.editor.duplicate"
|
||||
)}
|
||||
@@ -370,7 +323,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
? html`
|
||||
<ha-dropdown-item
|
||||
value="take_control"
|
||||
.disabled=${this._readOnly}
|
||||
.disabled=${this.readOnly}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.take_control"
|
||||
@@ -382,7 +335,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
|
||||
<ha-dropdown-item value="toggle_yaml_mode">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.edit_${this._mode === "gui" ? "yaml" : "ui"}`
|
||||
`ui.panel.config.automation.editor.edit_${this.mode === "gui" ? "yaml" : "ui"}`
|
||||
)}
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||
</ha-dropdown-item>
|
||||
@@ -390,7 +343,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<ha-dropdown-item
|
||||
.disabled=${this._readOnly || !this.scriptId}
|
||||
.disabled=${this.readOnly || !this.scriptId}
|
||||
value="delete"
|
||||
.variant=${this.scriptId ? "danger" : "default"}
|
||||
>
|
||||
@@ -403,8 +356,8 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
</ha-svg-icon>
|
||||
</ha-dropdown-item>
|
||||
</ha-dropdown>
|
||||
<div class=${this._mode === "yaml" ? "yaml-mode" : ""}>
|
||||
${this._mode === "gui"
|
||||
<div class=${this.mode === "yaml" ? "yaml-mode" : ""}>
|
||||
${this.mode === "gui"
|
||||
? html`
|
||||
<div>
|
||||
${useBlueprint
|
||||
@@ -413,10 +366,10 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
.config=${this._config}
|
||||
.disabled=${this._readOnly}
|
||||
.saving=${this._saving}
|
||||
.dirty=${this._dirty}
|
||||
.config=${this.config}
|
||||
.disabled=${this.readOnly}
|
||||
.saving=${this.saving}
|
||||
.dirty=${this.dirty}
|
||||
@value-changed=${this._valueChanged}
|
||||
@save-script=${this._handleSaveScript}
|
||||
></blueprint-script-editor>
|
||||
@@ -426,16 +379,16 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
.config=${this._config}
|
||||
.disabled=${this._readOnly}
|
||||
.dirty=${this._dirty}
|
||||
.saving=${this._saving}
|
||||
.config=${this.config}
|
||||
.disabled=${this.readOnly}
|
||||
.dirty=${this.dirty}
|
||||
.saving=${this.saving}
|
||||
@value-changed=${this._valueChanged}
|
||||
@editor-save=${this._handleSaveScript}
|
||||
@save-script=${this._handleSaveScript}
|
||||
>
|
||||
<div class="alert-wrapper" slot="alerts">
|
||||
${this._errors || stateObj?.state === UNAVAILABLE
|
||||
${this.errors || stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-alert
|
||||
alert-type="error"
|
||||
.title=${stateObj?.state === UNAVAILABLE
|
||||
@@ -444,7 +397,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
)
|
||||
: undefined}
|
||||
>
|
||||
${this._errors || this._validationErrors}
|
||||
${this.errors || this.validationErrors}
|
||||
${stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-svg-icon
|
||||
slot="icon"
|
||||
@@ -453,7 +406,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
: nothing}
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
${this._blueprintConfig
|
||||
${this.blueprintConfig
|
||||
? html`<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.confirm_take_control"
|
||||
@@ -461,21 +414,21 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
<div slot="action" style="display: flex;">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._takeControlSave}
|
||||
@click=${this.takeControlSave}
|
||||
>${this.hass.localize(
|
||||
"ui.common.yes"
|
||||
)}</ha-button
|
||||
>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._revertBlueprint}
|
||||
@click=${this.revertBlueprint}
|
||||
>${this.hass.localize(
|
||||
"ui.common.no"
|
||||
)}</ha-button
|
||||
>
|
||||
</div>
|
||||
</ha-alert>`
|
||||
: this._readOnly
|
||||
: this.readOnly
|
||||
? html`<ha-alert
|
||||
alert-type="warning"
|
||||
dismissable
|
||||
@@ -498,11 +451,11 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
`}
|
||||
</div>
|
||||
`
|
||||
: this._mode === "yaml"
|
||||
: this.mode === "yaml"
|
||||
? html`<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this._preprocessYaml()}
|
||||
.readOnly=${this._readOnly}
|
||||
.readOnly=${this.readOnly}
|
||||
disable-fullscreen
|
||||
@value-changed=${this._yamlChanged}
|
||||
@editor-save=${this._handleSaveScript}
|
||||
@@ -510,9 +463,9 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
></ha-yaml-editor>
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
class=${!this._readOnly && this._dirty ? "dirty" : ""}
|
||||
class=${!this.readOnly && this.dirty ? "dirty" : ""}
|
||||
.label=${this.hass.localize("ui.common.save")}
|
||||
.disabled=${this._saving}
|
||||
.disabled=${this.saving}
|
||||
extended
|
||||
@click=${this._handleSaveScript}
|
||||
>
|
||||
@@ -551,26 +504,26 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
const entity = this.entityRegistry.find(
|
||||
(ent) => ent.platform === "script" && ent.unique_id === this.scriptId
|
||||
);
|
||||
this._entityId = entity?.entity_id;
|
||||
this.currentEntityId = entity?.entity_id;
|
||||
}
|
||||
|
||||
if (changedProps.has("scriptId") && !this.scriptId && this.hass) {
|
||||
const initData = getScriptEditorInitData();
|
||||
this._dirty = !!initData;
|
||||
this.dirty = !!initData;
|
||||
const baseConfig: Partial<ScriptConfig> = {};
|
||||
if (!initData || !("use_blueprint" in initData)) {
|
||||
baseConfig.sequence = [];
|
||||
}
|
||||
this._config = {
|
||||
this.config = {
|
||||
...baseConfig,
|
||||
...initData,
|
||||
} as ScriptConfig;
|
||||
this._readOnly = false;
|
||||
this.readOnly = false;
|
||||
}
|
||||
|
||||
if (changedProps.has("entityId") && this.entityId) {
|
||||
getScriptStateConfig(this.hass, this.entityId).then((c) => {
|
||||
this._config = normalizeScriptConfig(c.config);
|
||||
this.config = normalizeScriptConfig(c.config);
|
||||
this._checkValidation();
|
||||
});
|
||||
const regEntry = this.entityRegistry.find(
|
||||
@@ -579,25 +532,25 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
if (regEntry?.unique_id) {
|
||||
this.scriptId = regEntry.unique_id;
|
||||
}
|
||||
this._entityId = this.entityId;
|
||||
this._dirty = false;
|
||||
this._readOnly = true;
|
||||
this.currentEntityId = this.entityId;
|
||||
this.dirty = false;
|
||||
this.readOnly = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async _checkValidation() {
|
||||
this._validationErrors = undefined;
|
||||
if (!this._entityId || !this._config) {
|
||||
this.validationErrors = undefined;
|
||||
if (!this.currentEntityId || !this.config) {
|
||||
return;
|
||||
}
|
||||
const stateObj = this.hass.states[this._entityId];
|
||||
const stateObj = this.hass.states[this.currentEntityId];
|
||||
if (stateObj?.state !== UNAVAILABLE) {
|
||||
return;
|
||||
}
|
||||
const validation = await validateConfig(this.hass, {
|
||||
actions: this._config.sequence,
|
||||
actions: this.config.sequence,
|
||||
});
|
||||
this._validationErrors = (
|
||||
this.validationErrors = (
|
||||
Object.entries(validation) as Entries<typeof validation>
|
||||
).map(([key, value]) =>
|
||||
value.valid
|
||||
@@ -612,13 +565,13 @@ 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);
|
||||
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._entityId = entity?.entity_id;
|
||||
this.currentEntityId = entity?.entity_id;
|
||||
this._checkValidation();
|
||||
},
|
||||
(resp) => {
|
||||
@@ -647,19 +600,19 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
}
|
||||
|
||||
private _valueChanged(ev) {
|
||||
if (this._config) {
|
||||
this._undoRedoController.commit(this._config);
|
||||
if (this.config) {
|
||||
this._undoRedoController.commit(this.config);
|
||||
}
|
||||
|
||||
this._config = ev.detail.value;
|
||||
this._errors = undefined;
|
||||
this._dirty = true;
|
||||
this.config = ev.detail.value;
|
||||
this.errors = undefined;
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
private async _runScript() {
|
||||
if (hasScriptFields(this.hass, this._entityId!)) {
|
||||
if (hasScriptFields(this.hass, this.currentEntityId!)) {
|
||||
showMoreInfoDialog(this, {
|
||||
entityId: this._entityId!,
|
||||
entityId: this.currentEntityId!,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -667,20 +620,13 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
await triggerScript(this.hass, this.scriptId!);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.notification_toast.triggered", {
|
||||
name: this._config!.alias,
|
||||
name: this.config!.alias,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
private _showSettings() {
|
||||
showMoreInfoDialog(this, {
|
||||
entityId: this._entityId!,
|
||||
view: "settings",
|
||||
});
|
||||
}
|
||||
|
||||
private _editCategory() {
|
||||
if (!this._registryEntry) {
|
||||
if (!this.registryEntry) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.scene.picker.no_category_support"
|
||||
@@ -693,7 +639,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
}
|
||||
showAssignCategoryDialog(this, {
|
||||
scope: "script",
|
||||
entityReg: this._registryEntry,
|
||||
entityReg: this.registryEntry,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -730,7 +676,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
|
||||
private async _showTrace() {
|
||||
if (this.scriptId) {
|
||||
const result = await this._confirmUnsavedChanged();
|
||||
const result = await this.confirmUnsavedChanged();
|
||||
if (result) {
|
||||
navigate(`/config/script/trace/${this.scriptId}`);
|
||||
}
|
||||
@@ -738,47 +684,47 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
}
|
||||
|
||||
private _addFields() {
|
||||
if ("fields" in this._config!) {
|
||||
if ("fields" in this.config!) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._config) {
|
||||
this._undoRedoController.commit(this._config);
|
||||
if (this.config) {
|
||||
this._undoRedoController.commit(this.config);
|
||||
}
|
||||
|
||||
this._manualEditor?.addFields();
|
||||
this._dirty = true;
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
private _preprocessYaml() {
|
||||
return this._config;
|
||||
return this.config;
|
||||
}
|
||||
|
||||
private _yamlChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
this._dirty = true;
|
||||
this.dirty = true;
|
||||
if (!ev.detail.isValid) {
|
||||
this._yamlErrors = ev.detail.errorMsg;
|
||||
this.yamlErrors = ev.detail.errorMsg;
|
||||
return;
|
||||
}
|
||||
this._yamlErrors = undefined;
|
||||
this._config = ev.detail.value;
|
||||
this._errors = undefined;
|
||||
this.yamlErrors = undefined;
|
||||
this.config = ev.detail.value;
|
||||
this.errors = undefined;
|
||||
}
|
||||
|
||||
private async _confirmUnsavedChanged(): Promise<boolean> {
|
||||
if (!this._dirty) {
|
||||
protected async confirmUnsavedChanged(): Promise<boolean> {
|
||||
if (!this.dirty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return new Promise<boolean>((resolve) => {
|
||||
showAutomationSaveDialog(this, {
|
||||
config: this._config!,
|
||||
config: this.config!,
|
||||
domain: "script",
|
||||
updateConfig: async (config, entityRegistryUpdate) => {
|
||||
this._config = config;
|
||||
this._entityRegistryUpdate = entityRegistryUpdate;
|
||||
this._dirty = true;
|
||||
this.config = config;
|
||||
this.entityRegistryUpdate = entityRegistryUpdate;
|
||||
this.dirty = true;
|
||||
this.requestUpdate();
|
||||
|
||||
const id = this.scriptId || String(Date.now());
|
||||
@@ -794,8 +740,8 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
},
|
||||
onClose: () => resolve(false),
|
||||
onDiscard: () => resolve(true),
|
||||
entityRegistryUpdate: this._entityRegistryUpdate,
|
||||
entityRegistryEntry: this._registryEntry,
|
||||
entityRegistryUpdate: this.entityRegistryUpdate,
|
||||
entityRegistryEntry: this.registryEntry,
|
||||
title: this.hass.localize(
|
||||
this.scriptId
|
||||
? "ui.panel.config.script.editor.leave.unsaved_confirm_title"
|
||||
@@ -811,15 +757,8 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
});
|
||||
}
|
||||
|
||||
private _backTapped = async () => {
|
||||
const result = await this._confirmUnsavedChanged();
|
||||
if (result) {
|
||||
afterNextRender(() => goBack("/config"));
|
||||
}
|
||||
};
|
||||
|
||||
private async _takeControl() {
|
||||
const config = this._config as BlueprintScriptConfig;
|
||||
const config = this.config as BlueprintScriptConfig;
|
||||
|
||||
try {
|
||||
const result = await substituteBlueprint(
|
||||
@@ -835,35 +774,20 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
description: config.description,
|
||||
};
|
||||
|
||||
this._blueprintConfig = config;
|
||||
this._config = newConfig;
|
||||
if (this._mode === "yaml") {
|
||||
this.renderRoot.querySelector("ha-yaml-editor")?.setValue(this._config);
|
||||
this.blueprintConfig = config;
|
||||
this.config = newConfig;
|
||||
if (this.mode === "yaml") {
|
||||
this.renderRoot.querySelector("ha-yaml-editor")?.setValue(this.config);
|
||||
}
|
||||
this._readOnly = true;
|
||||
this._errors = undefined;
|
||||
this.readOnly = true;
|
||||
this.errors = undefined;
|
||||
} catch (err: any) {
|
||||
this._errors = err.message;
|
||||
this.errors = err.message;
|
||||
}
|
||||
}
|
||||
|
||||
private _revertBlueprint() {
|
||||
this._config = this._blueprintConfig;
|
||||
if (this._mode === "yaml") {
|
||||
this.renderRoot.querySelector("ha-yaml-editor")?.setValue(this._config);
|
||||
}
|
||||
this._blueprintConfig = undefined;
|
||||
this._readOnly = false;
|
||||
}
|
||||
|
||||
private _takeControlSave() {
|
||||
this._readOnly = false;
|
||||
this._dirty = true;
|
||||
this._blueprintConfig = undefined;
|
||||
}
|
||||
|
||||
private async _duplicate() {
|
||||
const result = this._readOnly
|
||||
const result = this.readOnly
|
||||
? await showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.script.picker.migrate_script"
|
||||
@@ -872,14 +796,14 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
"ui.panel.config.script.picker.migrate_script_description"
|
||||
),
|
||||
})
|
||||
: await this._confirmUnsavedChanged();
|
||||
: await this.confirmUnsavedChanged();
|
||||
if (result) {
|
||||
this._entityId = undefined;
|
||||
this.currentEntityId = undefined;
|
||||
showScriptEditor({
|
||||
...this._config,
|
||||
alias: this._readOnly
|
||||
? this._config?.alias
|
||||
: `${this._config?.alias} (${this.hass.localize(
|
||||
...this.config,
|
||||
alias: this.readOnly
|
||||
? this.config?.alias
|
||||
: `${this.config?.alias} (${this.hass.localize(
|
||||
"ui.panel.config.script.picker.duplicate"
|
||||
)})`,
|
||||
});
|
||||
@@ -893,7 +817,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.script.editor.delete_confirm_text",
|
||||
{ name: this._config?.alias }
|
||||
{ name: this.config?.alias }
|
||||
),
|
||||
confirmText: this.hass!.localize("ui.common.delete"),
|
||||
destructive: true,
|
||||
@@ -907,42 +831,20 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
goBack("/config");
|
||||
}
|
||||
|
||||
private async _switchUiMode() {
|
||||
if (this._yamlErrors) {
|
||||
const result = await showConfirmationDialog(this, {
|
||||
text: html`${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.switch_ui_yaml_error"
|
||||
)}<br /><br />${this._yamlErrors}`,
|
||||
confirmText: this.hass!.localize("ui.common.continue"),
|
||||
destructive: true,
|
||||
dismissText: this.hass!.localize("ui.common.cancel"),
|
||||
});
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._yamlErrors = undefined;
|
||||
this._mode = "gui";
|
||||
}
|
||||
|
||||
private _switchYamlMode() {
|
||||
this._mode = "yaml";
|
||||
}
|
||||
|
||||
private async _promptScriptAlias(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
showAutomationSaveDialog(this, {
|
||||
config: this._config!,
|
||||
config: this.config!,
|
||||
domain: "script",
|
||||
updateConfig: async (config, entityRegistryUpdate) => {
|
||||
this._config = config;
|
||||
this._entityRegistryUpdate = entityRegistryUpdate;
|
||||
this._dirty = true;
|
||||
this.config = config;
|
||||
this.entityRegistryUpdate = entityRegistryUpdate;
|
||||
this.dirty = true;
|
||||
this.requestUpdate();
|
||||
resolve(true);
|
||||
},
|
||||
onClose: () => resolve(false),
|
||||
entityRegistryUpdate: this._entityRegistryUpdate,
|
||||
entityRegistryUpdate: this.entityRegistryUpdate,
|
||||
entityRegistryEntry: this.entityRegistry.find(
|
||||
(entry) => entry.unique_id === this.scriptId
|
||||
),
|
||||
@@ -953,10 +855,10 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
private async _promptScriptMode(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
showAutomationModeDialog(this, {
|
||||
config: this._config!,
|
||||
config: this.config!,
|
||||
updateConfig: (config) => {
|
||||
this._config = config;
|
||||
this._dirty = true;
|
||||
this.config = config;
|
||||
this.dirty = true;
|
||||
this.requestUpdate();
|
||||
resolve();
|
||||
},
|
||||
@@ -966,9 +868,9 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
}
|
||||
|
||||
private async _handleSaveScript() {
|
||||
if (this._yamlErrors) {
|
||||
if (this.yamlErrors) {
|
||||
showToast(this, {
|
||||
message: this._yamlErrors,
|
||||
message: this.yamlErrors,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -980,9 +882,9 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
if (!saved) {
|
||||
return;
|
||||
}
|
||||
this._entityId = this._computeEntityIdFromAlias(this._config!.alias);
|
||||
this.currentEntityId = this._computeEntityIdFromAlias(this.config!.alias);
|
||||
}
|
||||
const id = this.scriptId || this._entityId || Date.now();
|
||||
const id = this.scriptId || this.currentEntityId || Date.now();
|
||||
|
||||
await this._saveScript(id);
|
||||
if (!this.scriptId) {
|
||||
@@ -991,13 +893,13 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
}
|
||||
|
||||
private async _saveScript(id): Promise<void> {
|
||||
this._saving = true;
|
||||
this.saving = true;
|
||||
|
||||
let entityRegPromise: Promise<EntityRegistryEntry> | undefined;
|
||||
if (this._entityRegistryUpdate !== undefined && !this.scriptId) {
|
||||
if (this.entityRegistryUpdate !== undefined && !this.scriptId) {
|
||||
this._newScriptId = id.toString();
|
||||
entityRegPromise = new Promise<EntityRegistryEntry>((resolve) => {
|
||||
this._entityRegCreated = resolve;
|
||||
this.entityRegCreated = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1005,11 +907,11 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
await this.hass!.callApi(
|
||||
"POST",
|
||||
"config/script/config/" + id,
|
||||
this._config
|
||||
this.config
|
||||
);
|
||||
|
||||
if (this._entityRegistryUpdate !== undefined) {
|
||||
let entityId = this._entityId;
|
||||
if (this.entityRegistryUpdate !== undefined) {
|
||||
let entityId = this.currentEntityId;
|
||||
|
||||
// wait for new script to appear in entity registry
|
||||
if (entityRegPromise) {
|
||||
@@ -1044,23 +946,23 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
if (entityId) {
|
||||
await updateEntityRegistryEntry(this.hass, entityId, {
|
||||
categories: {
|
||||
script: this._entityRegistryUpdate.category || null,
|
||||
script: this.entityRegistryUpdate.category || null,
|
||||
},
|
||||
labels: this._entityRegistryUpdate.labels || [],
|
||||
area_id: this._entityRegistryUpdate.area || null,
|
||||
labels: this.entityRegistryUpdate.labels || [],
|
||||
area_id: this.entityRegistryUpdate.area || null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._dirty = false;
|
||||
this.dirty = false;
|
||||
} catch (errors: any) {
|
||||
this._errors = errors.body?.message || errors.error || errors.body;
|
||||
this.errors = errors.body?.message || errors.error || errors.body;
|
||||
showToast(this, {
|
||||
message: errors.body?.message || errors.error || errors.body,
|
||||
});
|
||||
throw errors;
|
||||
} finally {
|
||||
this._saving = false;
|
||||
this.saving = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1077,14 +979,6 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
};
|
||||
}
|
||||
|
||||
protected get isDirty() {
|
||||
return this._dirty;
|
||||
}
|
||||
|
||||
protected async promptDiscardChanges() {
|
||||
return this._confirmUnsavedChanged();
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
private _collapseAll() {
|
||||
this._manualEditor?.collapseAll();
|
||||
@@ -1109,8 +1003,8 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
|
||||
private _applyUndoRedo(config: ScriptConfig) {
|
||||
this._manualEditor?.triggerCloseSidebar();
|
||||
this._config = config;
|
||||
this._dirty = true;
|
||||
this.config = config;
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
private _undo() {
|
||||
@@ -1139,7 +1033,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
this._showInfo();
|
||||
break;
|
||||
case "settings":
|
||||
this._showSettings();
|
||||
this.showSettings();
|
||||
break;
|
||||
case "category":
|
||||
this._editCategory();
|
||||
@@ -1163,11 +1057,11 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
this._takeControl();
|
||||
break;
|
||||
case "toggle_yaml_mode":
|
||||
if (this._mode === "gui") {
|
||||
this._switchYamlMode();
|
||||
if (this.mode === "gui") {
|
||||
this.switchYamlMode();
|
||||
break;
|
||||
}
|
||||
this._switchUiMode();
|
||||
this.switchUiMode();
|
||||
break;
|
||||
case "delete":
|
||||
this._deleteConfirm();
|
||||
@@ -1181,19 +1075,8 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
automationScriptEditorStyles,
|
||||
css`
|
||||
:host {
|
||||
--ha-automation-editor-max-width: var(
|
||||
--ha-automation-editor-width,
|
||||
1540px
|
||||
);
|
||||
}
|
||||
.yaml-mode {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
manual-script-editor,
|
||||
blueprint-script-editor {
|
||||
margin: 0 auto;
|
||||
@@ -1244,29 +1127,9 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
ha-yaml-editor {
|
||||
flex-grow: 1;
|
||||
--actions-border-radius: var(--ha-border-radius-square);
|
||||
--code-mirror-height: 100%;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
span[slot="introduction"] a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
ha-fab {
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
bottom: calc(-80px - var(--safe-area-inset-bottom));
|
||||
transition: bottom 0.3s;
|
||||
}
|
||||
ha-fab.dirty {
|
||||
bottom: calc(16px + var(--safe-area-inset-bottom, 0px));
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
margin: 16px 0;
|
||||
@@ -1280,15 +1143,6 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
.header a {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-tooltip ha-svg-icon {
|
||||
width: 12px;
|
||||
}
|
||||
ha-tooltip .shortcut {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user