mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-09 10:59:50 +00:00
Automation editor sidebar (#26413)
This commit is contained in:
4
src/common/dom/prevent_default_stop_propagation.ts
Normal file
4
src/common/dom/prevent_default_stop_propagation.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const preventDefaultStopPropagation = (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
};
|
||||||
@@ -10,8 +10,8 @@ import {
|
|||||||
} from "../../data/device_automation";
|
} from "../../data/device_automation";
|
||||||
import type { EntityRegistryEntry } from "../../data/entity_registry";
|
import type { EntityRegistryEntry } from "../../data/entity_registry";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "../ha-list-item";
|
import "../ha-md-select-option";
|
||||||
import "../ha-select";
|
import "../ha-md-select";
|
||||||
import { stopPropagation } from "../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||||
|
|
||||||
const NO_AUTOMATION_KEY = "NO_AUTOMATION";
|
const NO_AUTOMATION_KEY = "NO_AUTOMATION";
|
||||||
@@ -100,35 +100,35 @@ export abstract class HaDeviceAutomationPicker<
|
|||||||
}
|
}
|
||||||
const value = this._value;
|
const value = this._value;
|
||||||
return html`
|
return html`
|
||||||
<ha-select
|
<ha-md-select
|
||||||
.label=${this.label}
|
.label=${this.label}
|
||||||
.value=${value}
|
.value=${value}
|
||||||
@selected=${this._automationChanged}
|
@change=${this._automationChanged}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
.disabled=${this._automations.length === 0}
|
.disabled=${this._automations.length === 0}
|
||||||
>
|
>
|
||||||
${value === NO_AUTOMATION_KEY
|
${value === NO_AUTOMATION_KEY
|
||||||
? html`<ha-list-item .value=${NO_AUTOMATION_KEY}>
|
? html`<ha-md-select-option .value=${NO_AUTOMATION_KEY}>
|
||||||
${this.NO_AUTOMATION_TEXT}
|
${this.NO_AUTOMATION_TEXT}
|
||||||
</ha-list-item>`
|
</ha-md-select-option>`
|
||||||
: ""}
|
: nothing}
|
||||||
${value === UNKNOWN_AUTOMATION_KEY
|
${value === UNKNOWN_AUTOMATION_KEY
|
||||||
? html`<ha-list-item .value=${UNKNOWN_AUTOMATION_KEY}>
|
? html`<ha-md-select-option .value=${UNKNOWN_AUTOMATION_KEY}>
|
||||||
${this.UNKNOWN_AUTOMATION_TEXT}
|
${this.UNKNOWN_AUTOMATION_TEXT}
|
||||||
</ha-list-item>`
|
</ha-md-select-option>`
|
||||||
: ""}
|
: nothing}
|
||||||
${this._automations.map(
|
${this._automations.map(
|
||||||
(automation, idx) => html`
|
(automation, idx) => html`
|
||||||
<ha-list-item .value=${`${automation.device_id}_${idx}`}>
|
<ha-md-select-option .value=${`${automation.device_id}_${idx}`}>
|
||||||
${this._localizeDeviceAutomation(
|
${this._localizeDeviceAutomation(
|
||||||
this.hass,
|
this.hass,
|
||||||
this._entityReg,
|
this._entityReg,
|
||||||
automation
|
automation
|
||||||
)}
|
)}
|
||||||
</ha-list-item>
|
</ha-md-select-option>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</ha-select>
|
</ha-md-select>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
148
src/components/ha-automation-row.ts
Normal file
148
src/components/ha-automation-row.ts
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import { mdiChevronUp } from "@mdi/js";
|
||||||
|
import type { TemplateResult } from "lit";
|
||||||
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import "./ha-icon-button";
|
||||||
|
|
||||||
|
@customElement("ha-automation-row")
|
||||||
|
export class HaAutomationRow extends LitElement {
|
||||||
|
@property({ attribute: "left-chevron", type: Boolean })
|
||||||
|
public leftChevron = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true })
|
||||||
|
public collapsed = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true })
|
||||||
|
public selected = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true })
|
||||||
|
public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true, attribute: "building-block" })
|
||||||
|
public buildingBlock = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="row"
|
||||||
|
tabindex="0"
|
||||||
|
role="button"
|
||||||
|
@keydown=${this._handleKeydown}
|
||||||
|
>
|
||||||
|
${this.leftChevron
|
||||||
|
? html`
|
||||||
|
<ha-icon-button
|
||||||
|
class="expand-button"
|
||||||
|
.path=${mdiChevronUp}
|
||||||
|
@click=${this._handleExpand}
|
||||||
|
@keydown=${this._handleExpand}
|
||||||
|
></ha-icon-button>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
<div class="leading-icon-wrapper">
|
||||||
|
<slot name="leading-icon"></slot>
|
||||||
|
</div>
|
||||||
|
<slot class="header" name="header"></slot>
|
||||||
|
<slot name="icons"></slot>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleExpand(ev) {
|
||||||
|
if (ev.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
fireEvent(this, "toggle-collapsed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleKeydown(ev: KeyboardEvent): Promise<void> {
|
||||||
|
if (ev.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.key !== "Enter" && ev.key !== " ") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
this.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 8px;
|
||||||
|
min-height: 48px;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
font-weight: var(--ha-font-weight-medium);
|
||||||
|
outline: none;
|
||||||
|
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
||||||
|
}
|
||||||
|
.row:focus {
|
||||||
|
outline: var(--wa-focus-ring);
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
.expand-button {
|
||||||
|
transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
color: var(--ha-color-on-neutral-quiet);
|
||||||
|
}
|
||||||
|
:host([building-block]) .leading-icon-wrapper {
|
||||||
|
background-color: var(--ha-color-fill-neutral-loud-resting);
|
||||||
|
border-radius: var(--ha-border-radius-md);
|
||||||
|
padding: 4px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
::slotted([slot="leading-icon"]) {
|
||||||
|
color: var(--ha-color-on-neutral-quiet);
|
||||||
|
}
|
||||||
|
:host([building-block]) ::slotted([slot="leading-icon"]) {
|
||||||
|
--mdc-icon-size: 20px;
|
||||||
|
color: var(--white-color);
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
:host([collapsed]) .expand-button {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
:host([selected]) .row,
|
||||||
|
:host([selected]) .row:focus {
|
||||||
|
outline: solid;
|
||||||
|
outline-color: var(--primary-color);
|
||||||
|
outline-offset: -2px;
|
||||||
|
outline-width: 2px;
|
||||||
|
}
|
||||||
|
:host([disabled]) .row {
|
||||||
|
border-top-right-radius: var(--ha-border-radius-square);
|
||||||
|
border-top-left-radius: var(--ha-border-radius-square);
|
||||||
|
}
|
||||||
|
::slotted([slot="header"]) {
|
||||||
|
flex: 1;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
margin: 0 12px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-row": HaAutomationRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"toggle-collapsed": undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
import { ContextProvider, consume } from "@lit/context";
|
import { consume, ContextProvider } from "@lit/context";
|
||||||
|
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
|
||||||
import { fullEntitiesContext } from "../../data/context";
|
import { fullEntitiesContext } from "../../data/context";
|
||||||
import type { Action } from "../../data/script";
|
|
||||||
import { migrateAutomationAction } from "../../data/script";
|
|
||||||
import type { ActionSelector } from "../../data/selector";
|
|
||||||
import "../../panels/config/automation/action/ha-automation-action";
|
|
||||||
import type { HomeAssistant } from "../../types";
|
|
||||||
import {
|
import {
|
||||||
subscribeEntityRegistry,
|
subscribeEntityRegistry,
|
||||||
type EntityRegistryEntry,
|
type EntityRegistryEntry,
|
||||||
} from "../../data/entity_registry";
|
} from "../../data/entity_registry";
|
||||||
|
import type { Action } from "../../data/script";
|
||||||
|
import { migrateAutomationAction } from "../../data/script";
|
||||||
|
import type { ActionSelector } from "../../data/selector";
|
||||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||||
|
import "../../panels/config/automation/action/ha-automation-action";
|
||||||
|
import type { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
@customElement("ha-selector-action")
|
@customElement("ha-selector-action")
|
||||||
export class HaActionSelector extends SubscribeMixin(LitElement) {
|
export class HaActionSelector extends SubscribeMixin(LitElement) {
|
||||||
@@ -69,6 +69,7 @@ export class HaActionSelector extends SubscribeMixin(LitElement) {
|
|||||||
.actions=${this._actions(this.value)}
|
.actions=${this._actions(this.value)}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
.optionsInSidebar=${!!this.selector.action?.optionsInSidebar}
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export class HaConditionSelector extends LitElement {
|
|||||||
.conditions=${this.value || []}
|
.conditions=${this.value || []}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
.optionsInSidebar=${!!this.selector.condition?.optionsInSidebar}
|
||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,3 +90,19 @@ export const isService = (key: string | undefined): boolean | undefined =>
|
|||||||
|
|
||||||
export const getService = (key: string): string =>
|
export const getService = (key: string): string =>
|
||||||
key.substring(SERVICE_PREFIX.length);
|
key.substring(SERVICE_PREFIX.length);
|
||||||
|
|
||||||
|
export const ACTION_BUILDING_BLOCKS = [
|
||||||
|
"choose",
|
||||||
|
"if",
|
||||||
|
"parallel",
|
||||||
|
"sequence",
|
||||||
|
"repeat_while",
|
||||||
|
"repeat_until",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Building blocks that have options in the sidebar
|
||||||
|
export const ACTION_COMBINED_BLOCKS = [
|
||||||
|
"repeat_count", // virtual repeat variant
|
||||||
|
"repeat_for_each", // virtual repeat variant
|
||||||
|
"wait_for_trigger",
|
||||||
|
];
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ import type {
|
|||||||
HassEntityAttributeBase,
|
HassEntityAttributeBase,
|
||||||
HassEntityBase,
|
HassEntityBase,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
import { navigate } from "../common/navigate";
|
|
||||||
import { ensureArray } from "../common/array/ensure-array";
|
import { ensureArray } from "../common/array/ensure-array";
|
||||||
|
import { navigate } from "../common/navigate";
|
||||||
|
import { createSearchParam } from "../common/url/search-params";
|
||||||
import type { Context, HomeAssistant } from "../types";
|
import type { Context, HomeAssistant } from "../types";
|
||||||
import type { BlueprintInput } from "./blueprint";
|
import type { BlueprintInput } from "./blueprint";
|
||||||
import type { DeviceCondition, DeviceTrigger } from "./device_automation";
|
import type { DeviceCondition, DeviceTrigger } from "./device_automation";
|
||||||
import type { Action, MODES } from "./script";
|
import type { Action, MODES } from "./script";
|
||||||
import { migrateAutomationAction } from "./script";
|
import { migrateAutomationAction } from "./script";
|
||||||
import { createSearchParam } from "../common/url/search-params";
|
import { CONDITION_BUILDING_BLOCKS } from "./condition";
|
||||||
|
|
||||||
export const AUTOMATION_DEFAULT_MODE: (typeof MODES)[number] = "single";
|
export const AUTOMATION_DEFAULT_MODE: (typeof MODES)[number] = "single";
|
||||||
export const AUTOMATION_DEFAULT_MAX = 10;
|
export const AUTOMATION_DEFAULT_MAX = 10;
|
||||||
@@ -325,7 +326,7 @@ export const expandConditionWithShorthand = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const condition of ["and", "or", "not"]) {
|
for (const condition of CONDITION_BUILDING_BLOCKS) {
|
||||||
if (condition in cond) {
|
if (condition in cond) {
|
||||||
return {
|
return {
|
||||||
condition,
|
condition,
|
||||||
|
|||||||
@@ -50,3 +50,5 @@ export const CONDITION_GROUPS: AutomationElementGroup = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const CONDITION_BUILDING_BLOCKS = ["and", "or", "not"];
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ export type Selector =
|
|||||||
| BackupLocationSelector;
|
| BackupLocationSelector;
|
||||||
|
|
||||||
export interface ActionSelector {
|
export interface ActionSelector {
|
||||||
action: {} | null;
|
action: {
|
||||||
|
optionsInSidebar?: boolean;
|
||||||
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddonSelector {
|
export interface AddonSelector {
|
||||||
@@ -130,7 +132,9 @@ export interface ColorTempSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ConditionSelector {
|
export interface ConditionSelector {
|
||||||
condition: {} | null;
|
condition: {
|
||||||
|
optionsInSidebar?: boolean;
|
||||||
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConversationAgentSelector {
|
export interface ConversationAgentSelector {
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-yaml-editor";
|
||||||
|
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
|
||||||
|
import { migrateAutomationAction, type Action } from "../../../../data/script";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import "../ha-automation-editor-warning";
|
||||||
|
import { editorStyles } from "../styles";
|
||||||
|
import { getAutomationActionType } from "./ha-automation-action-row";
|
||||||
|
|
||||||
|
@customElement("ha-automation-action-editor")
|
||||||
|
export default class HaAutomationActionEditor extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) action!: Action;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public yamlMode = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public indent = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true }) public selected = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" }) public inSidebar = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "supported" }) public uiSupported =
|
||||||
|
false;
|
||||||
|
|
||||||
|
@query("ha-yaml-editor") public yamlEditor?: HaYamlEditor;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const yamlMode = this.yamlMode || !this.uiSupported;
|
||||||
|
const type = getAutomationActionType(this.action);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class=${classMap({
|
||||||
|
"card-content": true,
|
||||||
|
disabled:
|
||||||
|
this.disabled || (this.action.enabled === false && !this.yamlMode),
|
||||||
|
yaml: yamlMode,
|
||||||
|
indent: this.indent,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
${yamlMode
|
||||||
|
? html`
|
||||||
|
${!this.uiSupported
|
||||||
|
? html`
|
||||||
|
<ha-automation-editor-warning
|
||||||
|
.alertTitle=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.unsupported_action"
|
||||||
|
)}
|
||||||
|
.localize=${this.hass.localize}
|
||||||
|
></ha-automation-editor-warning>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
<ha-yaml-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.defaultValue=${this.action}
|
||||||
|
@value-changed=${this._onYamlChange}
|
||||||
|
.readOnly=${this.disabled}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<div @value-changed=${this._onUiChanged}>
|
||||||
|
${dynamicElement(`ha-automation-action-${type}`, {
|
||||||
|
hass: this.hass,
|
||||||
|
action: this.action,
|
||||||
|
disabled: this.disabled,
|
||||||
|
narrow: this.narrow,
|
||||||
|
optionsInSidebar: this.indent,
|
||||||
|
indent: this.indent,
|
||||||
|
inSidebar: this.inSidebar,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onYamlChange(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!ev.detail.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: migrateAutomationAction(ev.detail.value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onUiChanged(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const value = {
|
||||||
|
...(this.action.alias ? { alias: this.action.alias } : {}),
|
||||||
|
...ev.detail.value,
|
||||||
|
};
|
||||||
|
fireEvent(this, "value-changed", { value });
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = editorStyles;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-action-editor": HaAutomationActionEditor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,28 +15,35 @@ import {
|
|||||||
mdiStopCircleOutline,
|
mdiStopCircleOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, html, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import memoizeOne from "memoize-one";
|
||||||
import { storage } from "../../../../common/decorators/storage";
|
import { storage } from "../../../../common/decorators/storage";
|
||||||
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
||||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
import "../../../../components/ha-alert";
|
import "../../../../components/ha-automation-row";
|
||||||
import "../../../../components/ha-md-button-menu";
|
|
||||||
import "../../../../components/ha-md-menu-item";
|
|
||||||
import "../../../../components/ha-md-divider";
|
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-expansion-panel";
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
|
import "../../../../components/ha-md-button-menu";
|
||||||
|
import "../../../../components/ha-md-divider";
|
||||||
|
import "../../../../components/ha-md-menu-item";
|
||||||
import "../../../../components/ha-service-icon";
|
import "../../../../components/ha-service-icon";
|
||||||
import "../../../../components/ha-tooltip";
|
import "../../../../components/ha-tooltip";
|
||||||
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
|
import {
|
||||||
import { ACTION_ICONS, YAML_ONLY_ACTION_TYPES } from "../../../../data/action";
|
ACTION_BUILDING_BLOCKS,
|
||||||
import type { AutomationClipboard } from "../../../../data/automation";
|
ACTION_COMBINED_BLOCKS,
|
||||||
|
ACTION_ICONS,
|
||||||
|
YAML_ONLY_ACTION_TYPES,
|
||||||
|
} from "../../../../data/action";
|
||||||
|
import type {
|
||||||
|
AutomationClipboard,
|
||||||
|
Condition,
|
||||||
|
} from "../../../../data/automation";
|
||||||
import { validateConfig } from "../../../../data/config";
|
import { validateConfig } from "../../../../data/config";
|
||||||
import {
|
import {
|
||||||
floorsContext,
|
floorsContext,
|
||||||
@@ -46,11 +53,12 @@ import {
|
|||||||
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
|
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
|
||||||
import type { FloorRegistryEntry } from "../../../../data/floor_registry";
|
import type { FloorRegistryEntry } from "../../../../data/floor_registry";
|
||||||
import type { LabelRegistryEntry } from "../../../../data/label_registry";
|
import type { LabelRegistryEntry } from "../../../../data/label_registry";
|
||||||
import type { Action, NonConditionAction } from "../../../../data/script";
|
import type {
|
||||||
import {
|
Action,
|
||||||
getActionType,
|
NonConditionAction,
|
||||||
migrateAutomationAction,
|
RepeatAction,
|
||||||
} from "../../../../data/script";
|
} from "../../../../data/script";
|
||||||
|
import { getActionType } from "../../../../data/script";
|
||||||
import { describeAction } from "../../../../data/script_i18n";
|
import { describeAction } from "../../../../data/script_i18n";
|
||||||
import { callExecuteScript } from "../../../../data/service";
|
import { callExecuteScript } from "../../../../data/service";
|
||||||
import {
|
import {
|
||||||
@@ -58,9 +66,12 @@ import {
|
|||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
showPromptDialog,
|
showPromptDialog,
|
||||||
} from "../../../../dialogs/generic/show-dialog-box";
|
} from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { showToast } from "../../../../util/toast";
|
import { showToast } from "../../../../util/toast";
|
||||||
|
import "../ha-automation-editor-warning";
|
||||||
|
import { rowStyles } from "../styles";
|
||||||
|
import "./ha-automation-action-editor";
|
||||||
|
import type HaAutomationActionEditor from "./ha-automation-action-editor";
|
||||||
import "./types/ha-automation-action-choose";
|
import "./types/ha-automation-action-choose";
|
||||||
import "./types/ha-automation-action-condition";
|
import "./types/ha-automation-action-condition";
|
||||||
import "./types/ha-automation-action-delay";
|
import "./types/ha-automation-action-delay";
|
||||||
@@ -69,28 +80,31 @@ import "./types/ha-automation-action-event";
|
|||||||
import "./types/ha-automation-action-if";
|
import "./types/ha-automation-action-if";
|
||||||
import "./types/ha-automation-action-parallel";
|
import "./types/ha-automation-action-parallel";
|
||||||
import "./types/ha-automation-action-play_media";
|
import "./types/ha-automation-action-play_media";
|
||||||
import "./types/ha-automation-action-repeat";
|
import { getRepeatType } from "./types/ha-automation-action-repeat";
|
||||||
import "./types/ha-automation-action-sequence";
|
import "./types/ha-automation-action-sequence";
|
||||||
import "./types/ha-automation-action-service";
|
import "./types/ha-automation-action-service";
|
||||||
import "./types/ha-automation-action-set_conversation_response";
|
import "./types/ha-automation-action-set_conversation_response";
|
||||||
import "./types/ha-automation-action-stop";
|
import "./types/ha-automation-action-stop";
|
||||||
import "./types/ha-automation-action-wait_for_trigger";
|
import "./types/ha-automation-action-wait_for_trigger";
|
||||||
import "./types/ha-automation-action-wait_template";
|
import "./types/ha-automation-action-wait_template";
|
||||||
|
import { CONDITION_BUILDING_BLOCKS } from "../../../../data/condition";
|
||||||
|
|
||||||
export const getType = (action: Action | undefined) => {
|
export const getAutomationActionType = memoizeOne(
|
||||||
if (!action) {
|
(action: Action | undefined) => {
|
||||||
return undefined;
|
if (!action) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if ("action" in action) {
|
||||||
|
return getActionType(action) as "action" | "play_media";
|
||||||
|
}
|
||||||
|
if (CONDITION_BUILDING_BLOCKS.some((key) => key in action)) {
|
||||||
|
return "condition" as const;
|
||||||
|
}
|
||||||
|
return Object.keys(ACTION_ICONS).find(
|
||||||
|
(option) => option in action
|
||||||
|
) as keyof typeof ACTION_ICONS;
|
||||||
}
|
}
|
||||||
if ("action" in action) {
|
);
|
||||||
return getActionType(action) as "action" | "play_media";
|
|
||||||
}
|
|
||||||
if (["and", "or", "not"].some((key) => key in action)) {
|
|
||||||
return "condition" as const;
|
|
||||||
}
|
|
||||||
return Object.keys(ACTION_ICONS).find(
|
|
||||||
(option) => option in action
|
|
||||||
) as keyof typeof ACTION_ICONS;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ActionElement extends LitElement {
|
export interface ActionElement extends LitElement {
|
||||||
action: Action;
|
action: Action;
|
||||||
@@ -118,8 +132,6 @@ export const handleChangeEvent = (element: ActionElement, ev: CustomEvent) => {
|
|||||||
fireEvent(element, "value-changed", { value: newAction });
|
fireEvent(element, "value-changed", { value: newAction });
|
||||||
};
|
};
|
||||||
|
|
||||||
const preventDefault = (ev) => ev.preventDefault();
|
|
||||||
|
|
||||||
@customElement("ha-automation-action-row")
|
@customElement("ha-automation-action-row")
|
||||||
export default class HaAutomationActionRow extends LitElement {
|
export default class HaAutomationActionRow extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -134,6 +146,9 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public last?: boolean;
|
@property({ type: Boolean }) public last?: boolean;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" })
|
||||||
|
public optionsInSidebar = false;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "automationClipboard",
|
key: "automationClipboard",
|
||||||
state: false,
|
state: false,
|
||||||
@@ -154,19 +169,27 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
@consume({ context: floorsContext, subscribe: true })
|
@consume({ context: floorsContext, subscribe: true })
|
||||||
_floorReg!: Record<string, FloorRegistryEntry>;
|
_floorReg!: Record<string, FloorRegistryEntry>;
|
||||||
|
|
||||||
@state() private _warnings?: string[];
|
|
||||||
|
|
||||||
@state() private _uiModeAvailable = true;
|
@state() private _uiModeAvailable = true;
|
||||||
|
|
||||||
@state() private _yamlMode = false;
|
@state() private _yamlMode = false;
|
||||||
|
|
||||||
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
|
@state() private _selected = false;
|
||||||
|
|
||||||
|
@state() private _collapsed = false;
|
||||||
|
|
||||||
|
@state() private _warnings?: string[];
|
||||||
|
|
||||||
|
@query("ha-automation-action-editor")
|
||||||
|
private actionEditor?: HaAutomationActionEditor;
|
||||||
|
|
||||||
protected willUpdate(changedProperties: PropertyValues) {
|
protected willUpdate(changedProperties: PropertyValues) {
|
||||||
|
if (changedProperties.has("yamlMode")) {
|
||||||
|
this._warnings = undefined;
|
||||||
|
}
|
||||||
if (!changedProperties.has("action")) {
|
if (!changedProperties.has("action")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const type = getType(this.action);
|
const type = getAutomationActionType(this.action);
|
||||||
this._uiModeAvailable =
|
this._uiModeAvailable =
|
||||||
type !== undefined && !YAML_ONLY_ACTION_TYPES.has(type as any);
|
type !== undefined && !YAML_ONLY_ACTION_TYPES.has(type as any);
|
||||||
if (!this._uiModeAvailable && !this._yamlMode) {
|
if (!this._uiModeAvailable && !this._yamlMode) {
|
||||||
@@ -174,23 +197,207 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues) {
|
private _renderRow() {
|
||||||
if (!changedProperties.has("action")) {
|
const type = getAutomationActionType(this.action);
|
||||||
return;
|
|
||||||
}
|
return html`
|
||||||
if (this._yamlMode) {
|
${type === "service" && "action" in this.action && this.action.action
|
||||||
const yamlEditor = this._yamlEditor;
|
? html`
|
||||||
if (yamlEditor && yamlEditor.value !== this.action) {
|
<ha-service-icon
|
||||||
yamlEditor.setValue(this.action);
|
slot="leading-icon"
|
||||||
}
|
class="action-icon"
|
||||||
}
|
.hass=${this.hass}
|
||||||
|
.service=${this.action.action}
|
||||||
|
></ha-service-icon>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="leading-icon"
|
||||||
|
class="action-icon"
|
||||||
|
.path=${ACTION_ICONS[type!]}
|
||||||
|
></ha-svg-icon>
|
||||||
|
`}
|
||||||
|
<h3 slot="header">
|
||||||
|
${capitalizeFirstLetter(
|
||||||
|
describeAction(
|
||||||
|
this.hass,
|
||||||
|
this._entityReg,
|
||||||
|
this._labelReg,
|
||||||
|
this._floorReg,
|
||||||
|
this.action
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<slot name="icons" slot="icons"></slot>
|
||||||
|
|
||||||
|
${type !== "condition" &&
|
||||||
|
(this.action as NonConditionAction).continue_on_error === true
|
||||||
|
? html`<ha-tooltip
|
||||||
|
slot="icons"
|
||||||
|
.content=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.continue_on_error"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiAlertCircleCheck}></ha-svg-icon>
|
||||||
|
</ha-tooltip>`
|
||||||
|
: nothing}
|
||||||
|
|
||||||
|
<ha-md-button-menu
|
||||||
|
slot="icons"
|
||||||
|
@click=${preventDefaultStopPropagation}
|
||||||
|
@keydown=${stopPropagation}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
positioning="fixed"
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
.path=${mdiDotsVertical}
|
||||||
|
></ha-icon-button>
|
||||||
|
<ha-md-menu-item .clickAction=${this._runAction}>
|
||||||
|
${this.hass.localize("ui.panel.config.automation.editor.actions.run")}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiPlay}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
${!this.optionsInSidebar
|
||||||
|
? html` <ha-md-menu-item
|
||||||
|
.clickAction=${this._renameAction}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.rename"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>`
|
||||||
|
: nothing}
|
||||||
|
|
||||||
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._duplicateAction}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.duplicate"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentDuplicate}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._copyAction}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.copy"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._cutAction}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.cut"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._moveUp}
|
||||||
|
.disabled=${this.disabled || !!this.first}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||||
|
></ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._moveDown}
|
||||||
|
.disabled=${this.disabled || !!this.last}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.panel.config.automation.editor.move_down")}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||||
|
></ha-md-menu-item>
|
||||||
|
|
||||||
|
${!this.optionsInSidebar
|
||||||
|
? html`<ha-md-menu-item
|
||||||
|
.clickAction=${this._toggleYamlMode}
|
||||||
|
.disabled=${!this._uiModeAvailable || !!this._warnings}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>`
|
||||||
|
: nothing}
|
||||||
|
|
||||||
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._onDisable}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.action.enabled === false
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.enable"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.disable"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${this.action.enabled === false
|
||||||
|
? mdiPlayCircleOutline
|
||||||
|
: mdiStopCircleOutline}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
<ha-md-menu-item
|
||||||
|
class="warning"
|
||||||
|
.clickAction=${this._onDelete}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class="warning"
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiDelete}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
</ha-md-button-menu>
|
||||||
|
|
||||||
|
${!this.optionsInSidebar
|
||||||
|
? html`${this._warnings
|
||||||
|
? html`<ha-automation-editor-warning
|
||||||
|
.localize=${this.hass.localize}
|
||||||
|
.warnings=${this._warnings}
|
||||||
|
>
|
||||||
|
</ha-automation-editor-warning>`
|
||||||
|
: nothing}
|
||||||
|
<ha-automation-action-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.action=${this.action}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.yamlMode=${this._yamlMode}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.uiSupported=${this._uiSupported(type!)}
|
||||||
|
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
||||||
|
></ha-automation-action-editor>`
|
||||||
|
: nothing}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.action) return nothing;
|
if (!this.action) return nothing;
|
||||||
|
|
||||||
const type = getType(this.action);
|
const type = getAutomationActionType(this.action);
|
||||||
const yamlMode = this._yamlMode;
|
|
||||||
|
const blockType =
|
||||||
|
type === "repeat"
|
||||||
|
? `repeat_${getRepeatType((this.action as RepeatAction).repeat)}`
|
||||||
|
: type;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card outlined>
|
<ha-card outlined>
|
||||||
@@ -203,245 +410,57 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
<ha-expansion-panel left-chevron>
|
${this.optionsInSidebar
|
||||||
${type === "service" && "action" in this.action && this.action.action
|
? html`<ha-automation-row
|
||||||
? html`
|
.disabled=${this.action.enabled === false}
|
||||||
<ha-service-icon
|
@click=${this._toggleSidebar}
|
||||||
slot="leading-icon"
|
.leftChevron=${[
|
||||||
class="action-icon"
|
...ACTION_BUILDING_BLOCKS,
|
||||||
.hass=${this.hass}
|
...ACTION_COMBINED_BLOCKS,
|
||||||
.service=${this.action.action}
|
].includes(blockType!)}
|
||||||
></ha-service-icon>
|
.collapsed=${this._collapsed}
|
||||||
`
|
.selected=${this._selected}
|
||||||
: html`
|
@toggle-collapsed=${this._toggleCollapse}
|
||||||
<ha-svg-icon
|
.buildingBlock=${[
|
||||||
slot="leading-icon"
|
...ACTION_BUILDING_BLOCKS,
|
||||||
class="action-icon"
|
...ACTION_COMBINED_BLOCKS,
|
||||||
.path=${ACTION_ICONS[type!]}
|
].includes(blockType!)}
|
||||||
></ha-svg-icon>
|
>${this._renderRow()}</ha-automation-row
|
||||||
`}
|
>`
|
||||||
<h3 slot="header">
|
: html`
|
||||||
${capitalizeFirstLetter(
|
<ha-expansion-panel left-chevron>
|
||||||
describeAction(
|
${this._renderRow()}
|
||||||
this.hass,
|
</ha-expansion-panel>
|
||||||
this._entityReg,
|
`}
|
||||||
this._labelReg,
|
|
||||||
this._floorReg,
|
|
||||||
this.action
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<slot name="icons" slot="icons"></slot>
|
|
||||||
|
|
||||||
${type !== "condition" &&
|
|
||||||
(this.action as NonConditionAction).continue_on_error === true
|
|
||||||
? html`<ha-tooltip
|
|
||||||
slot="icons"
|
|
||||||
.content=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.continue_on_error"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ha-svg-icon .path=${mdiAlertCircleCheck}></ha-svg-icon>
|
|
||||||
</ha-tooltip>`
|
|
||||||
: nothing}
|
|
||||||
|
|
||||||
<ha-md-button-menu
|
|
||||||
slot="icons"
|
|
||||||
@click=${preventDefault}
|
|
||||||
@keydown=${stopPropagation}
|
|
||||||
@closed=${stopPropagation}
|
|
||||||
positioning="fixed"
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
slot="trigger"
|
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
|
||||||
.path=${mdiDotsVertical}
|
|
||||||
></ha-icon-button>
|
|
||||||
<ha-md-menu-item .clickAction=${this._runAction}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.run"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiPlay}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._renameAction}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.rename"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._duplicateAction}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.duplicate"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="start"
|
|
||||||
.path=${mdiContentDuplicate}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._copyAction}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.copy"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._cutAction}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.cut"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._moveUp}
|
|
||||||
.disabled=${this.disabled || this.first}
|
|
||||||
>
|
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
|
||||||
></ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._moveDown}
|
|
||||||
.disabled=${this.disabled || this.last}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.move_down"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
|
||||||
></ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._toggleYamlMode}
|
|
||||||
.disabled=${!this._uiModeAvailable}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
`ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._onDisable}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.action.enabled === false
|
|
||||||
? this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.enable"
|
|
||||||
)
|
|
||||||
: this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.disable"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="start"
|
|
||||||
.path=${this.action.enabled === false
|
|
||||||
? mdiPlayCircleOutline
|
|
||||||
: mdiStopCircleOutline}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
<ha-md-menu-item
|
|
||||||
class="warning"
|
|
||||||
.clickAction=${this._onDelete}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
class="warning"
|
|
||||||
slot="start"
|
|
||||||
.path=${mdiDelete}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
</ha-md-button-menu>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class=${classMap({
|
|
||||||
"card-content": true,
|
|
||||||
disabled: this.action.enabled === false,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
${this._warnings
|
|
||||||
? html`<ha-alert
|
|
||||||
alert-type="warning"
|
|
||||||
.title=${this.hass.localize(
|
|
||||||
"ui.errors.config.editor_not_supported"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
${this._warnings!.length > 0 &&
|
|
||||||
this._warnings![0] !== undefined
|
|
||||||
? html` <ul>
|
|
||||||
${this._warnings!.map(
|
|
||||||
(warning) => html`<li>${warning}</li>`
|
|
||||||
)}
|
|
||||||
</ul>`
|
|
||||||
: ""}
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.errors.config.edit_in_yaml_supported"
|
|
||||||
)}
|
|
||||||
</ha-alert>`
|
|
||||||
: ""}
|
|
||||||
${yamlMode
|
|
||||||
? html`
|
|
||||||
${type === undefined
|
|
||||||
? html`
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.unsupported_action"
|
|
||||||
)}
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<ha-yaml-editor
|
|
||||||
.hass=${this.hass}
|
|
||||||
.defaultValue=${this.action}
|
|
||||||
.readOnly=${this.disabled}
|
|
||||||
@value-changed=${this._onYamlChange}
|
|
||||||
></ha-yaml-editor>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<div
|
|
||||||
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
|
||||||
@value-changed=${this._onUiChanged}
|
|
||||||
>
|
|
||||||
${dynamicElement(`ha-automation-action-${type}`, {
|
|
||||||
hass: this.hass,
|
|
||||||
action: this.action,
|
|
||||||
narrow: this.narrow,
|
|
||||||
disabled: this.disabled,
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
`}
|
|
||||||
</div>
|
|
||||||
</ha-expansion-panel>
|
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
|
||||||
|
${this.optionsInSidebar &&
|
||||||
|
([...ACTION_BUILDING_BLOCKS, ...ACTION_COMBINED_BLOCKS].includes(
|
||||||
|
blockType!
|
||||||
|
) ||
|
||||||
|
(blockType === "condition" &&
|
||||||
|
CONDITION_BUILDING_BLOCKS.includes(
|
||||||
|
(this.action as Condition).condition
|
||||||
|
))) &&
|
||||||
|
!this._collapsed
|
||||||
|
? html`<ha-automation-action-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.action=${this.action}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.uiSupported=${this._uiSupported(type!)}
|
||||||
|
indent
|
||||||
|
.selected=${this._selected}
|
||||||
|
@value-changed=${this._onValueChange}
|
||||||
|
></ha-automation-action-editor>`
|
||||||
|
: nothing}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleUiModeNotAvailable(ev: CustomEvent) {
|
private _onValueChange(event: CustomEvent) {
|
||||||
// Prevent possible parent action-row from switching to yamlMode
|
// reload sidebar if sort, deleted,... happend
|
||||||
ev.stopPropagation();
|
if (this._selected && this.optionsInSidebar) {
|
||||||
|
this.openSidebar(event.detail.value);
|
||||||
this._warnings = handleStructError(this.hass, ev.detail).warnings;
|
|
||||||
if (!this._yamlMode) {
|
|
||||||
this._yamlMode = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,8 +475,10 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
const enabled = !(this.action.enabled ?? true);
|
const enabled = !(this.action.enabled ?? true);
|
||||||
const value = { ...this.action, enabled };
|
const value = { ...this.action, enabled };
|
||||||
fireEvent(this, "value-changed", { value });
|
fireEvent(this, "value-changed", { value });
|
||||||
if (this._yamlMode) {
|
this.openSidebar(value); // refresh sidebar
|
||||||
this._yamlEditor?.setValue(value);
|
|
||||||
|
if (this._yamlMode && !this.optionsInSidebar) {
|
||||||
|
this.actionEditor?.yamlEditor?.setValue(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -508,36 +529,18 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
destructive: true,
|
destructive: true,
|
||||||
confirm: () => {
|
confirm: () => {
|
||||||
fireEvent(this, "value-changed", { value: null });
|
fireEvent(this, "value-changed", { value: null });
|
||||||
|
if (this._selected) {
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private _onYamlChange(ev: CustomEvent) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
if (!ev.detail.isValid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fireEvent(this, "value-changed", {
|
|
||||||
value: migrateAutomationAction(ev.detail.value),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onUiChanged(ev: CustomEvent) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
const value = {
|
|
||||||
...(this.action.alias ? { alias: this.action.alias } : {}),
|
|
||||||
...ev.detail.value,
|
|
||||||
};
|
|
||||||
fireEvent(this, "value-changed", { value });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _switchUiMode() {
|
private _switchUiMode() {
|
||||||
this._warnings = undefined;
|
|
||||||
this._yamlMode = false;
|
this._yamlMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _switchYamlMode() {
|
private _switchYamlMode() {
|
||||||
this._warnings = undefined;
|
|
||||||
this._yamlMode = true;
|
this._yamlMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,8 +577,11 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
if (this._yamlMode) {
|
|
||||||
this._yamlEditor?.setValue(value);
|
if (this._selected && this.optionsInSidebar) {
|
||||||
|
this.openSidebar(value); // refresh sidebar
|
||||||
|
} else if (this._yamlMode) {
|
||||||
|
this.actionEditor?.yamlEditor?.setValue(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -591,6 +597,9 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
private _cutAction = () => {
|
private _cutAction = () => {
|
||||||
this._setClipboard();
|
this._setClipboard();
|
||||||
fireEvent(this, "value-changed", { value: null });
|
fireEvent(this, "value-changed", { value: null });
|
||||||
|
if (this._selected) {
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _moveUp = () => {
|
private _moveUp = () => {
|
||||||
@@ -607,82 +616,78 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
} else {
|
} else {
|
||||||
this._switchYamlMode();
|
this._switchYamlMode();
|
||||||
}
|
}
|
||||||
this.expand();
|
|
||||||
|
if (!this.optionsInSidebar) {
|
||||||
|
this.expand();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private _handleUiModeNotAvailable(ev: CustomEvent) {
|
||||||
|
this._warnings = handleStructError(this.hass, ev.detail).warnings;
|
||||||
|
if (!this._yamlMode) {
|
||||||
|
this._yamlMode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleSidebar(ev: Event) {
|
||||||
|
ev?.stopPropagation();
|
||||||
|
|
||||||
|
if (this._selected) {
|
||||||
|
this._selected = false;
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openSidebar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public openSidebar(action?: Action): void {
|
||||||
|
if (this.narrow) {
|
||||||
|
this.scrollIntoView();
|
||||||
|
}
|
||||||
|
const sidebarAction = action ?? this.action;
|
||||||
|
const actionType = getAutomationActionType(sidebarAction);
|
||||||
|
|
||||||
|
fireEvent(this, "open-sidebar", {
|
||||||
|
save: (value) => {
|
||||||
|
fireEvent(this, "value-changed", { value });
|
||||||
|
},
|
||||||
|
close: () => {
|
||||||
|
this._selected = false;
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
},
|
||||||
|
rename: () => {
|
||||||
|
this._renameAction();
|
||||||
|
},
|
||||||
|
toggleYamlMode: () => {
|
||||||
|
this._toggleYamlMode();
|
||||||
|
return this._yamlMode;
|
||||||
|
},
|
||||||
|
disable: this._onDisable,
|
||||||
|
delete: this._onDelete,
|
||||||
|
config: sidebarAction,
|
||||||
|
type: "action",
|
||||||
|
uiSupported: actionType ? this._uiSupported(actionType) : false,
|
||||||
|
yamlMode: this._yamlMode,
|
||||||
|
});
|
||||||
|
this._selected = true;
|
||||||
|
}
|
||||||
|
|
||||||
public expand() {
|
public expand() {
|
||||||
this.updateComplete.then(() => {
|
this.updateComplete.then(() => {
|
||||||
this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = true;
|
this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
private _uiSupported = memoizeOne(
|
||||||
return [
|
(type: string) =>
|
||||||
haStyle,
|
customElements.get(`ha-automation-action-${type}`) !== undefined
|
||||||
css`
|
);
|
||||||
ha-icon-button {
|
|
||||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
private _toggleCollapse() {
|
||||||
}
|
this._collapsed = !this._collapsed;
|
||||||
.disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
ha-expansion-panel {
|
|
||||||
--expansion-panel-summary-padding: 0 0 0 8px;
|
|
||||||
--expansion-panel-content-padding: 0;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
.action-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
@media (min-width: 870px) {
|
|
||||||
.action-icon {
|
|
||||||
display: inline-block;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.card-content {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
.disabled-bar {
|
|
||||||
background: var(--divider-color, #e0e0e0);
|
|
||||||
text-align: center;
|
|
||||||
border-top-right-radius: calc(
|
|
||||||
var(--ha-card-border-radius, 12px) - var(
|
|
||||||
--ha-card-border-width,
|
|
||||||
1px
|
|
||||||
)
|
|
||||||
);
|
|
||||||
border-top-left-radius: calc(
|
|
||||||
var(--ha-card-border-radius, 12px) - var(
|
|
||||||
--ha-card-border-width,
|
|
||||||
1px
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
.warning ul {
|
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
ha-md-menu-item > ha-svg-icon {
|
|
||||||
--mdc-icon-size: 24px;
|
|
||||||
}
|
|
||||||
ha-tooltip {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
:host([highlight]) ha-card {
|
|
||||||
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
|
|
||||||
--shadow-focus: 0 0 0 1px var(--state-inactive-color);
|
|
||||||
border-color: var(--state-inactive-color);
|
|
||||||
box-shadow: var(--shadow-default), var(--shadow-focus);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static styles = rowStyles;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ import { nextRender } from "../../../../common/util/render-status";
|
|||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
import "../../../../components/ha-sortable";
|
import "../../../../components/ha-sortable";
|
||||||
import "../../../../components/ha-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
import { getService, isService } from "../../../../data/action";
|
import {
|
||||||
|
ACTION_BUILDING_BLOCKS,
|
||||||
|
getService,
|
||||||
|
isService,
|
||||||
|
} from "../../../../data/action";
|
||||||
import type { AutomationClipboard } from "../../../../data/automation";
|
import type { AutomationClipboard } from "../../../../data/automation";
|
||||||
import type { Action } from "../../../../data/script";
|
import type { Action } from "../../../../data/script";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -21,7 +25,7 @@ import {
|
|||||||
showAddAutomationElementDialog,
|
showAddAutomationElementDialog,
|
||||||
} from "../show-add-automation-element-dialog";
|
} from "../show-add-automation-element-dialog";
|
||||||
import type HaAutomationActionRow from "./ha-automation-action-row";
|
import type HaAutomationActionRow from "./ha-automation-action-row";
|
||||||
import { getType } from "./ha-automation-action-row";
|
import { getAutomationActionType } from "./ha-automation-action-row";
|
||||||
|
|
||||||
@customElement("ha-automation-action")
|
@customElement("ha-automation-action")
|
||||||
export default class HaAutomationAction extends LitElement {
|
export default class HaAutomationAction extends LitElement {
|
||||||
@@ -37,6 +41,9 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public highlightedActions?: Action[];
|
@property({ attribute: false }) public highlightedActions?: Action[];
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" }) public optionsInSidebar =
|
||||||
|
false;
|
||||||
|
|
||||||
@state() private _showReorder = false;
|
@state() private _showReorder = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
@@ -98,6 +105,7 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
@value-changed=${this._actionChanged}
|
@value-changed=${this._actionChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
?highlight=${this.highlightedActions?.includes(action)}
|
?highlight=${this.highlightedActions?.includes(action)}
|
||||||
|
.optionsInSidebar=${this.optionsInSidebar}
|
||||||
>
|
>
|
||||||
${this._showReorder && !this.disabled
|
${this._showReorder && !this.disabled
|
||||||
? html`
|
? html`
|
||||||
@@ -148,7 +156,17 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
"ha-automation-action-row:last-of-type"
|
"ha-automation-action-row:last-of-type"
|
||||||
)!;
|
)!;
|
||||||
row.updateComplete.then(() => {
|
row.updateComplete.then(() => {
|
||||||
row.expand();
|
// on new condition open the settings in the sidebar, except for building blocks
|
||||||
|
const type = getAutomationActionType(row.action);
|
||||||
|
if (
|
||||||
|
type &&
|
||||||
|
this.optionsInSidebar &&
|
||||||
|
!ACTION_BUILDING_BLOCKS.includes(type)
|
||||||
|
) {
|
||||||
|
row.openSidebar();
|
||||||
|
} else if (!this.optionsInSidebar) {
|
||||||
|
row.expand();
|
||||||
|
}
|
||||||
row.scrollIntoView();
|
row.scrollIntoView();
|
||||||
row.focus();
|
row.focus();
|
||||||
});
|
});
|
||||||
@@ -168,7 +186,7 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
showAddAutomationElementDialog(this, {
|
showAddAutomationElementDialog(this, {
|
||||||
type: "action",
|
type: "action",
|
||||||
add: this._addAction,
|
add: this._addAction,
|
||||||
clipboardItem: getType(this._clipboard?.action),
|
clipboardItem: getAutomationActionType(this._clipboard?.action),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +194,7 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
showAddAutomationElementDialog(this, {
|
showAddAutomationElementDialog(this, {
|
||||||
type: "action",
|
type: "action",
|
||||||
add: this._addAction,
|
add: this._addAction,
|
||||||
clipboardItem: getType(this._clipboard?.action),
|
clipboardItem: getAutomationActionType(this._clipboard?.action),
|
||||||
group: "building_blocks",
|
group: "building_blocks",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -272,6 +290,7 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
// Ensure action is removed even after update
|
// Ensure action is removed even after update
|
||||||
const actions = this.actions.filter((a) => a !== action);
|
const actions = this.actions.filter((a) => a !== action);
|
||||||
fireEvent(this, "value-changed", { value: actions });
|
fireEvent(this, "value-changed", { value: actions });
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
}
|
}
|
||||||
|
|
||||||
private _actionChanged(ev: CustomEvent) {
|
private _actionChanged(ev: CustomEvent) {
|
||||||
@@ -303,15 +322,18 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
.actions {
|
.actions {
|
||||||
padding: 16px;
|
padding: 16px 0 16px 16px;
|
||||||
margin: -16px;
|
margin: -16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
:host([root]) .actions {
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
.sortable-ghost {
|
.sortable-ghost {
|
||||||
background: none;
|
background: none;
|
||||||
border-radius: var(--ha-card-border-radius, 12px);
|
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
||||||
}
|
}
|
||||||
.sortable-drag {
|
.sortable-drag {
|
||||||
background: none;
|
background: none;
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import type { Action, ChooseAction, Option } from "../../../../../data/script";
|
|||||||
import { haStyle } from "../../../../../resources/styles";
|
import { haStyle } from "../../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import "../../option/ha-automation-option";
|
import "../../option/ha-automation-option";
|
||||||
import type { ActionElement } from "../ha-automation-action-row";
|
|
||||||
import "../ha-automation-action";
|
import "../ha-automation-action";
|
||||||
|
import type { ActionElement } from "../ha-automation-action-row";
|
||||||
|
|
||||||
@customElement("ha-automation-action-choose")
|
@customElement("ha-automation-action-choose")
|
||||||
export class HaChooseAction extends LitElement implements ActionElement {
|
export class HaChooseAction extends LitElement implements ActionElement {
|
||||||
@@ -20,6 +20,8 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public narrow = false;
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public indent = false;
|
||||||
|
|
||||||
@state() private _showDefault = false;
|
@state() private _showDefault = false;
|
||||||
|
|
||||||
public static get defaultConfig(): ChooseAction {
|
public static get defaultConfig(): ChooseAction {
|
||||||
@@ -38,6 +40,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
@value-changed=${this._optionsChanged}
|
@value-changed=${this._optionsChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
.optionsInSidebar=${this.indent}
|
||||||
></ha-automation-option>
|
></ha-automation-option>
|
||||||
|
|
||||||
${this._showDefault || action.default
|
${this._showDefault || action.default
|
||||||
@@ -53,6 +56,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
@value-changed=${this._defaultChanged}
|
@value-changed=${this._defaultChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
.optionsInSidebar=${this.indent}
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
@@ -8,10 +8,24 @@ import "../../../../../components/ha-list-item";
|
|||||||
import "../../../../../components/ha-select";
|
import "../../../../../components/ha-select";
|
||||||
import type { HaSelect } from "../../../../../components/ha-select";
|
import type { HaSelect } from "../../../../../components/ha-select";
|
||||||
import type { Condition } from "../../../../../data/automation";
|
import type { Condition } from "../../../../../data/automation";
|
||||||
import { CONDITION_ICONS } from "../../../../../data/condition";
|
import {
|
||||||
|
CONDITION_BUILDING_BLOCKS,
|
||||||
|
CONDITION_ICONS,
|
||||||
|
} from "../../../../../data/condition";
|
||||||
import type { Entries, HomeAssistant } from "../../../../../types";
|
import type { Entries, HomeAssistant } from "../../../../../types";
|
||||||
import "../../condition/ha-automation-condition-editor";
|
import "../../condition/ha-automation-condition-editor";
|
||||||
import type { ActionElement } from "../ha-automation-action-row";
|
import type { ActionElement } from "../ha-automation-action-row";
|
||||||
|
import "../../condition/types/ha-automation-condition-and";
|
||||||
|
import "../../condition/types/ha-automation-condition-device";
|
||||||
|
import "../../condition/types/ha-automation-condition-not";
|
||||||
|
import "../../condition/types/ha-automation-condition-numeric_state";
|
||||||
|
import "../../condition/types/ha-automation-condition-or";
|
||||||
|
import "../../condition/types/ha-automation-condition-state";
|
||||||
|
import "../../condition/types/ha-automation-condition-sun";
|
||||||
|
import "../../condition/types/ha-automation-condition-template";
|
||||||
|
import "../../condition/types/ha-automation-condition-time";
|
||||||
|
import "../../condition/types/ha-automation-condition-trigger";
|
||||||
|
import "../../condition/types/ha-automation-condition-zone";
|
||||||
|
|
||||||
@customElement("ha-automation-action-condition")
|
@customElement("ha-automation-action-condition")
|
||||||
export class HaConditionAction extends LitElement implements ActionElement {
|
export class HaConditionAction extends LitElement implements ActionElement {
|
||||||
@@ -21,36 +35,63 @@ export class HaConditionAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public action!: Condition;
|
@property({ attribute: false }) public action!: Condition;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" }) public inSidebar = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "indent" }) public indent = false;
|
||||||
|
|
||||||
public static get defaultConfig(): Omit<Condition, "state" | "entity_id"> {
|
public static get defaultConfig(): Omit<Condition, "state" | "entity_id"> {
|
||||||
return { condition: "state" };
|
return { condition: "state" };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
|
const buildingBlock = CONDITION_BUILDING_BLOCKS.includes(
|
||||||
|
this.action.condition
|
||||||
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-select
|
${this.inSidebar || (!this.inSidebar && !this.indent)
|
||||||
fixedMenuPosition
|
? html`
|
||||||
.label=${this.hass.localize(
|
<ha-select
|
||||||
"ui.panel.config.automation.editor.conditions.type_select"
|
fixedMenuPosition
|
||||||
)}
|
.label=${this.hass.localize(
|
||||||
.disabled=${this.disabled}
|
"ui.panel.config.automation.editor.conditions.type_select"
|
||||||
.value=${this.action.condition}
|
)}
|
||||||
naturalMenuWidth
|
.disabled=${this.disabled}
|
||||||
@selected=${this._typeChanged}
|
.value=${this.action.condition}
|
||||||
>
|
naturalMenuWidth
|
||||||
${this._processedTypes(this.hass.localize).map(
|
@selected=${this._typeChanged}
|
||||||
([opt, label, icon]) => html`
|
>
|
||||||
<ha-list-item .value=${opt} graphic="icon">
|
${this._processedTypes(this.hass.localize).map(
|
||||||
${label}<ha-svg-icon slot="graphic" .path=${icon}></ha-svg-icon
|
([opt, label, icon]) => html`
|
||||||
></ha-list-item>
|
<ha-list-item .value=${opt} graphic="icon">
|
||||||
|
${label}<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${icon}
|
||||||
|
></ha-svg-icon
|
||||||
|
></ha-list-item>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ha-select>
|
||||||
`
|
`
|
||||||
)}
|
: nothing}
|
||||||
</ha-select>
|
${(this.indent && buildingBlock) ||
|
||||||
<ha-automation-condition-editor
|
(this.inSidebar && !buildingBlock) ||
|
||||||
.condition=${this.action}
|
(!this.indent && !this.inSidebar)
|
||||||
.disabled=${this.disabled}
|
? html`
|
||||||
.hass=${this.hass}
|
<ha-automation-condition-editor
|
||||||
@value-changed=${this._conditionChanged}
|
.condition=${this.action}
|
||||||
></ha-automation-condition-editor>
|
.disabled=${this.disabled}
|
||||||
|
.hass=${this.hass}
|
||||||
|
@value-changed=${this._conditionChanged}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.uiSupported=${this._uiSupported(this.action.condition)}
|
||||||
|
.indent=${this.indent}
|
||||||
|
action
|
||||||
|
></ha-automation-condition-editor>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +141,11 @@ export class HaConditionAction extends LitElement implements ActionElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _uiSupported = memoizeOne(
|
||||||
|
(type: string) =>
|
||||||
|
customElements.get(`ha-automation-condition-${type}`) !== undefined
|
||||||
|
);
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
ha-select {
|
ha-select {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export class HaIfAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public narrow = false;
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public indent = false;
|
||||||
|
|
||||||
@state() private _showElse = false;
|
@state() private _showElse = false;
|
||||||
|
|
||||||
public static get defaultConfig(): IfAction {
|
public static get defaultConfig(): IfAction {
|
||||||
@@ -44,6 +46,7 @@ export class HaIfAction extends LitElement implements ActionElement {
|
|||||||
@value-changed=${this._ifChanged}
|
@value-changed=${this._ifChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
.optionsInSidebar=${this.indent}
|
||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
@@ -57,6 +60,7 @@ export class HaIfAction extends LitElement implements ActionElement {
|
|||||||
@value-changed=${this._thenChanged}
|
@value-changed=${this._thenChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
.optionsInSidebar=${this.indent}
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
${this._showElse || action.else
|
${this._showElse || action.else
|
||||||
? html`
|
? html`
|
||||||
@@ -71,9 +75,10 @@ export class HaIfAction extends LitElement implements ActionElement {
|
|||||||
@value-changed=${this._elseChanged}
|
@value-changed=${this._elseChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
.optionsInSidebar=${this.indent}
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
`
|
`
|
||||||
: html` <div class="link-button-row">
|
: html`<div class="link-button-row">
|
||||||
<button
|
<button
|
||||||
class="link"
|
class="link"
|
||||||
@click=${this._addElse}
|
@click=${this._addElse}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export class HaParallelAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public action!: ParallelAction;
|
@property({ attribute: false }) public action!: ParallelAction;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public indent = false;
|
||||||
|
|
||||||
public static get defaultConfig(): ParallelAction {
|
public static get defaultConfig(): ParallelAction {
|
||||||
return {
|
return {
|
||||||
parallel: [],
|
parallel: [],
|
||||||
@@ -35,6 +37,7 @@ export class HaParallelAction extends LitElement implements ActionElement {
|
|||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@value-changed=${this._actionsChanged}
|
@value-changed=${this._actionsChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.optionsInSidebar=${this.indent}
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ import type {
|
|||||||
} from "../../../../../components/ha-form/types";
|
} from "../../../../../components/ha-form/types";
|
||||||
|
|
||||||
const OPTIONS = ["count", "while", "until", "for_each"] as const;
|
const OPTIONS = ["count", "while", "until", "for_each"] as const;
|
||||||
|
type RepeatType = (typeof OPTIONS)[number];
|
||||||
|
|
||||||
const getType = (action) => OPTIONS.find((option) => option in action);
|
export const getRepeatType = (action: RepeatAction["repeat"]) =>
|
||||||
|
OPTIONS.find((option) => option in action);
|
||||||
|
|
||||||
@customElement("ha-automation-action-repeat")
|
@customElement("ha-automation-action-repeat")
|
||||||
export class HaRepeatAction extends LitElement implements ActionElement {
|
export class HaRepeatAction extends LitElement implements ActionElement {
|
||||||
@@ -27,16 +29,27 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
@property({ attribute: false }) public action!: RepeatAction;
|
@property({ attribute: false }) public action!: RepeatAction;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" }) public inSidebar = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "indent" }) public indent = false;
|
||||||
|
|
||||||
public static get defaultConfig(): RepeatAction {
|
public static get defaultConfig(): RepeatAction {
|
||||||
return { repeat: { count: 2, sequence: [] } };
|
return { repeat: { count: 2, sequence: [] } };
|
||||||
}
|
}
|
||||||
|
|
||||||
private _schema = memoizeOne(
|
private _schema = memoizeOne(
|
||||||
(type: string, template: boolean) =>
|
(
|
||||||
|
type: RepeatType,
|
||||||
|
template: boolean,
|
||||||
|
inSidebar: boolean,
|
||||||
|
indent: boolean
|
||||||
|
) =>
|
||||||
[
|
[
|
||||||
...(type === "count"
|
...(type === "count" && (inSidebar || (!inSidebar && !indent))
|
||||||
? ([
|
? ([
|
||||||
{
|
{
|
||||||
name: "count",
|
name: "count",
|
||||||
@@ -47,17 +60,20 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
|||||||
},
|
},
|
||||||
] as const satisfies readonly HaFormSchema[])
|
] as const satisfies readonly HaFormSchema[])
|
||||||
: []),
|
: []),
|
||||||
...(type === "until" || type === "while"
|
...((type === "until" || type === "while") &&
|
||||||
|
(indent || (!inSidebar && !indent))
|
||||||
? ([
|
? ([
|
||||||
{
|
{
|
||||||
name: type,
|
name: type,
|
||||||
selector: {
|
selector: {
|
||||||
condition: {},
|
condition: {
|
||||||
|
optionsInSidebar: indent,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] as const satisfies readonly HaFormSchema[])
|
] as const satisfies readonly HaFormSchema[])
|
||||||
: []),
|
: []),
|
||||||
...(type === "for_each"
|
...(type === "for_each" && (inSidebar || (!inSidebar && !indent))
|
||||||
? ([
|
? ([
|
||||||
{
|
{
|
||||||
name: "for_each",
|
name: "for_each",
|
||||||
@@ -66,23 +82,31 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
|||||||
},
|
},
|
||||||
] as const satisfies readonly HaFormSchema[])
|
] as const satisfies readonly HaFormSchema[])
|
||||||
: []),
|
: []),
|
||||||
{
|
...(indent || (!inSidebar && !indent)
|
||||||
name: "sequence",
|
? ([
|
||||||
selector: {
|
{
|
||||||
action: {},
|
name: "sequence",
|
||||||
},
|
selector: {
|
||||||
},
|
action: {
|
||||||
|
optionsInSidebar: indent,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as const satisfies readonly HaFormSchema[])
|
||||||
|
: []),
|
||||||
] as const satisfies readonly HaFormSchema[]
|
] as const satisfies readonly HaFormSchema[]
|
||||||
);
|
);
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const action = this.action.repeat;
|
const action = this.action.repeat;
|
||||||
const type = getType(action);
|
const type = getRepeatType(action);
|
||||||
const schema = this._schema(
|
const schema = this._schema(
|
||||||
type ?? "count",
|
type ?? "count",
|
||||||
"count" in action && typeof action.count === "string"
|
"count" in action && typeof action.count === "string"
|
||||||
? isTemplate(action.count)
|
? isTemplate(action.count)
|
||||||
: false
|
: false,
|
||||||
|
this.inSidebar,
|
||||||
|
this.indent
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = { ...action, type };
|
const data = { ...action, type };
|
||||||
@@ -93,6 +117,7 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
|||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.computeLabel=${this._computeLabelCallback}
|
.computeLabel=${this._computeLabelCallback}
|
||||||
|
.narrow=${this.narrow}
|
||||||
></ha-form>`;
|
></ha-form>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +127,7 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
const newType = newVal.type;
|
const newType = newVal.type;
|
||||||
delete newVal.type;
|
delete newVal.type;
|
||||||
const oldType = getType(this.action.repeat);
|
const oldType = getRepeatType(this.action.repeat);
|
||||||
|
|
||||||
if (newType !== oldType) {
|
if (newType !== oldType) {
|
||||||
if (newType === "count") {
|
if (newType === "count") {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export class HaSequenceAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public action!: SequenceAction;
|
@property({ attribute: false }) public action!: SequenceAction;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public indent = false;
|
||||||
|
|
||||||
public static get defaultConfig(): SequenceAction {
|
public static get defaultConfig(): SequenceAction {
|
||||||
return {
|
return {
|
||||||
sequence: [],
|
sequence: [],
|
||||||
@@ -35,6 +37,7 @@ export class HaSequenceAction extends LitElement implements ActionElement {
|
|||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@value-changed=${this._actionsChanged}
|
@value-changed=${this._actionsChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.optionsInSidebar=${this.indent}
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { ensureArray } from "../../../../../common/array/ensure-array";
|
import { ensureArray } from "../../../../../common/array/ensure-array";
|
||||||
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
|
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
|
||||||
@@ -24,6 +24,12 @@ export class HaWaitForTriggerAction
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" }) public inSidebar = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "indent" }) public indent = false;
|
||||||
|
|
||||||
public static get defaultConfig(): WaitForTriggerAction {
|
public static get defaultConfig(): WaitForTriggerAction {
|
||||||
return { wait_for_trigger: [] };
|
return { wait_for_trigger: [] };
|
||||||
}
|
}
|
||||||
@@ -32,34 +38,43 @@ export class HaWaitForTriggerAction
|
|||||||
const timeData = createDurationData(this.action.timeout);
|
const timeData = createDurationData(this.action.timeout);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-duration-input
|
${this.inSidebar || (!this.inSidebar && !this.indent)
|
||||||
.label=${this.hass.localize(
|
? html`
|
||||||
"ui.panel.config.automation.editor.actions.type.wait_for_trigger.timeout"
|
<ha-duration-input
|
||||||
)}
|
.label=${this.hass.localize(
|
||||||
.data=${timeData}
|
"ui.panel.config.automation.editor.actions.type.wait_for_trigger.timeout"
|
||||||
.disabled=${this.disabled}
|
)}
|
||||||
enable-millisecond
|
.data=${timeData}
|
||||||
@value-changed=${this._timeoutChanged}
|
.disabled=${this.disabled}
|
||||||
></ha-duration-input>
|
enable-millisecond
|
||||||
<ha-formfield
|
@value-changed=${this._timeoutChanged}
|
||||||
.disabled=${this.disabled}
|
></ha-duration-input>
|
||||||
.label=${this.hass.localize(
|
<ha-formfield
|
||||||
"ui.panel.config.automation.editor.actions.type.wait_for_trigger.continue_timeout"
|
.disabled=${this.disabled}
|
||||||
)}
|
.label=${this.hass.localize(
|
||||||
>
|
"ui.panel.config.automation.editor.actions.type.wait_for_trigger.continue_timeout"
|
||||||
<ha-switch
|
)}
|
||||||
.checked=${this.action.continue_on_timeout ?? true}
|
>
|
||||||
.disabled=${this.disabled}
|
<ha-switch
|
||||||
@change=${this._continueChanged}
|
.checked=${this.action.continue_on_timeout ?? true}
|
||||||
></ha-switch>
|
.disabled=${this.disabled}
|
||||||
</ha-formfield>
|
@change=${this._continueChanged}
|
||||||
<ha-automation-trigger
|
></ha-switch>
|
||||||
.triggers=${ensureArray(this.action.wait_for_trigger)}
|
</ha-formfield>
|
||||||
.hass=${this.hass}
|
`
|
||||||
.disabled=${this.disabled}
|
: nothing}
|
||||||
.name=${"wait_for_trigger"}
|
${this.indent || (!this.inSidebar && !this.indent)
|
||||||
@value-changed=${this._valueChanged}
|
? html`<ha-automation-trigger
|
||||||
></ha-automation-trigger>
|
class=${!this.inSidebar && !this.indent ? "expansion-panel" : ""}
|
||||||
|
.triggers=${ensureArray(this.action.wait_for_trigger)}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.name=${"wait_for_trigger"}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.optionsInSidebar=${this.indent}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
></ha-automation-trigger>`
|
||||||
|
: nothing}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +101,7 @@ export class HaWaitForTriggerAction
|
|||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
ha-automation-trigger {
|
ha-automation-trigger.expansion-panel {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -652,6 +652,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
ha-dialog {
|
ha-dialog {
|
||||||
--dialog-content-padding: 0;
|
--dialog-content-padding: 0;
|
||||||
--mdc-dialog-max-height: 60vh;
|
--mdc-dialog-max-height: 60vh;
|
||||||
|
--mdc-dialog-max-height: 60dvh;
|
||||||
}
|
}
|
||||||
@media all and (min-width: 550px) {
|
@media all and (min-width: 550px) {
|
||||||
ha-dialog {
|
ha-dialog {
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
|
import { mdiContentSave } from "@mdi/js";
|
||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { html, nothing } from "lit";
|
import { css, html, nothing, type CSSResultGroup } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-markdown";
|
import "../../../components/ha-markdown";
|
||||||
import type { BlueprintAutomationConfig } from "../../../data/automation";
|
import type { BlueprintAutomationConfig } from "../../../data/automation";
|
||||||
import { fetchBlueprints } from "../../../data/blueprint";
|
import { fetchBlueprints } from "../../../data/blueprint";
|
||||||
import { HaBlueprintGenericEditor } from "../blueprint/blueprint-generic-editor";
|
import { HaBlueprintGenericEditor } from "../blueprint/blueprint-generic-editor";
|
||||||
|
import { saveFabStyles } from "./styles";
|
||||||
|
|
||||||
@customElement("blueprint-automation-editor")
|
@customElement("blueprint-automation-editor")
|
||||||
export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor {
|
export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor {
|
||||||
@@ -14,6 +17,10 @@ export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor {
|
|||||||
|
|
||||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public saving = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public dirty = false;
|
||||||
|
|
||||||
protected get _config(): BlueprintAutomationConfig {
|
protected get _config(): BlueprintAutomationConfig {
|
||||||
return this.config;
|
return this.config;
|
||||||
}
|
}
|
||||||
@@ -47,9 +54,24 @@ export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor {
|
|||||||
></ha-markdown>`
|
></ha-markdown>`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.renderCard()}
|
${this.renderCard()}
|
||||||
|
|
||||||
|
<ha-fab
|
||||||
|
slot="fab"
|
||||||
|
class=${this.dirty ? "dirty" : ""}
|
||||||
|
.label=${this.hass.localize("ui.panel.config.automation.editor.save")}
|
||||||
|
.disabled=${this.saving}
|
||||||
|
extended
|
||||||
|
@click=${this._saveAutomation}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||||
|
</ha-fab>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _saveAutomation() {
|
||||||
|
fireEvent(this, "save-automation");
|
||||||
|
}
|
||||||
|
|
||||||
protected async _getBlueprints() {
|
protected async _getBlueprints() {
|
||||||
this._blueprints = await fetchBlueprints(this.hass, "automation");
|
this._blueprints = await fetchBlueprints(this.hass, "automation");
|
||||||
}
|
}
|
||||||
@@ -62,6 +84,24 @@ export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor {
|
|||||||
entity_id: this.stateObj.entity_id,
|
entity_id: this.stateObj.entity_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
HaBlueprintGenericEditor.styles,
|
||||||
|
saveFabStyles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
min-height: calc(100vh - 85px);
|
||||||
|
min-height: calc(100dvh - 85px);
|
||||||
|
}
|
||||||
|
ha-fab {
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
|
|||||||
@@ -1,24 +1,16 @@
|
|||||||
import { html, LitElement } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import "../../../../components/ha-yaml-editor";
|
import "../../../../components/ha-yaml-editor";
|
||||||
|
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
|
||||||
import type { Condition } from "../../../../data/automation";
|
import type { Condition } from "../../../../data/automation";
|
||||||
import { expandConditionWithShorthand } from "../../../../data/automation";
|
import { expandConditionWithShorthand } from "../../../../data/automation";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import "./types/ha-automation-condition-and";
|
import "../ha-automation-editor-warning";
|
||||||
import "./types/ha-automation-condition-device";
|
import { editorStyles } from "../styles";
|
||||||
import "./types/ha-automation-condition-not";
|
|
||||||
import "./types/ha-automation-condition-numeric_state";
|
|
||||||
import "./types/ha-automation-condition-or";
|
|
||||||
import "./types/ha-automation-condition-state";
|
|
||||||
import "./types/ha-automation-condition-sun";
|
|
||||||
import "./types/ha-automation-condition-template";
|
|
||||||
import "./types/ha-automation-condition-time";
|
|
||||||
import "./types/ha-automation-condition-trigger";
|
|
||||||
import "./types/ha-automation-condition-zone";
|
|
||||||
|
|
||||||
@customElement("ha-automation-condition-editor")
|
@customElement("ha-automation-condition-editor")
|
||||||
export default class HaAutomationConditionEditor extends LitElement {
|
export default class HaAutomationConditionEditor extends LitElement {
|
||||||
@@ -30,46 +22,71 @@ export default class HaAutomationConditionEditor extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public yamlMode = false;
|
@property({ attribute: false }) public yamlMode = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public indent = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true }) public selected = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "supported" }) public uiSupported =
|
||||||
|
false;
|
||||||
|
|
||||||
|
@query("ha-yaml-editor") public yamlEditor?: HaYamlEditor;
|
||||||
|
|
||||||
private _processedCondition = memoizeOne((condition) =>
|
private _processedCondition = memoizeOne((condition) =>
|
||||||
expandConditionWithShorthand(condition)
|
expandConditionWithShorthand(condition)
|
||||||
);
|
);
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const condition = this._processedCondition(this.condition);
|
const condition = this._processedCondition(this.condition);
|
||||||
const supported =
|
const yamlMode = this.yamlMode || !this.uiSupported;
|
||||||
customElements.get(`ha-automation-condition-${condition.condition}`) !==
|
|
||||||
undefined;
|
|
||||||
const yamlMode = this.yamlMode || !supported;
|
|
||||||
return html`
|
return html`
|
||||||
${yamlMode
|
<div
|
||||||
? html`
|
class=${classMap({
|
||||||
${!supported
|
"card-content": true,
|
||||||
? html`
|
disabled:
|
||||||
${this.hass.localize(
|
this.disabled ||
|
||||||
"ui.panel.config.automation.editor.conditions.unsupported_condition",
|
(this.condition.enabled === false && !this.yamlMode),
|
||||||
{ condition: condition.condition }
|
yaml: yamlMode,
|
||||||
)}
|
indent: this.indent,
|
||||||
`
|
})}
|
||||||
: ""}
|
>
|
||||||
<ha-yaml-editor
|
${yamlMode
|
||||||
.hass=${this.hass}
|
? html`
|
||||||
.defaultValue=${this.condition}
|
${!this.uiSupported
|
||||||
@value-changed=${this._onYamlChange}
|
? html`
|
||||||
.readOnly=${this.disabled}
|
<ha-automation-editor-warning
|
||||||
></ha-yaml-editor>
|
.alertTitle=${this.hass.localize(
|
||||||
`
|
"ui.panel.config.automation.editor.conditions.unsupported_condition",
|
||||||
: html`
|
{ condition: condition.condition }
|
||||||
<div @value-changed=${this._onUiChanged}>
|
)}
|
||||||
${dynamicElement(
|
.localize=${this.hass.localize}
|
||||||
`ha-automation-condition-${condition.condition}`,
|
></ha-automation-editor-warning>
|
||||||
{
|
`
|
||||||
hass: this.hass,
|
: nothing}
|
||||||
condition: condition,
|
<ha-yaml-editor
|
||||||
disabled: this.disabled,
|
.hass=${this.hass}
|
||||||
}
|
.defaultValue=${this.condition}
|
||||||
)}
|
@value-changed=${this._onYamlChange}
|
||||||
</div>
|
.readOnly=${this.disabled}
|
||||||
`}
|
></ha-yaml-editor>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<div @value-changed=${this._onUiChanged}>
|
||||||
|
${dynamicElement(
|
||||||
|
`ha-automation-condition-${condition.condition}`,
|
||||||
|
{
|
||||||
|
hass: this.hass,
|
||||||
|
condition: condition,
|
||||||
|
disabled: this.disabled,
|
||||||
|
optionsInSidebar: this.indent,
|
||||||
|
narrow: this.narrow,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +108,20 @@ export default class HaAutomationConditionEditor extends LitElement {
|
|||||||
fireEvent(this, "value-changed", { value });
|
fireEvent(this, "value-changed", { value });
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = haStyle;
|
static styles = [
|
||||||
|
editorStyles,
|
||||||
|
css`
|
||||||
|
:host([action]) .card-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
:host([action]) .card-content.indent {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
@@ -16,26 +16,32 @@ import {
|
|||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import { storage } from "../../../../common/decorators/storage";
|
import { storage } from "../../../../common/decorators/storage";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
||||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
import "../../../../components/ha-md-button-menu";
|
import "../../../../components/ha-automation-row";
|
||||||
import "../../../../components/ha-md-menu-item";
|
|
||||||
import "../../../../components/ha-md-divider";
|
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-expansion-panel";
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
|
import "../../../../components/ha-md-button-menu";
|
||||||
|
import "../../../../components/ha-md-divider";
|
||||||
|
import "../../../../components/ha-md-menu-item";
|
||||||
import type {
|
import type {
|
||||||
AutomationClipboard,
|
AutomationClipboard,
|
||||||
Condition,
|
Condition,
|
||||||
} from "../../../../data/automation";
|
} from "../../../../data/automation";
|
||||||
import { testCondition } from "../../../../data/automation";
|
import { testCondition } from "../../../../data/automation";
|
||||||
import { describeCondition } from "../../../../data/automation_i18n";
|
import { describeCondition } from "../../../../data/automation_i18n";
|
||||||
import { CONDITION_ICONS } from "../../../../data/condition";
|
import {
|
||||||
|
CONDITION_BUILDING_BLOCKS,
|
||||||
|
CONDITION_ICONS,
|
||||||
|
} from "../../../../data/condition";
|
||||||
import { validateConfig } from "../../../../data/config";
|
import { validateConfig } from "../../../../data/config";
|
||||||
import { fullEntitiesContext } from "../../../../data/context";
|
import { fullEntitiesContext } from "../../../../data/context";
|
||||||
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
|
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
|
||||||
@@ -44,16 +50,27 @@ import {
|
|||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
showPromptDialog,
|
showPromptDialog,
|
||||||
} from "../../../../dialogs/generic/show-dialog-box";
|
} from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import "../ha-automation-editor-warning";
|
||||||
|
import { rowStyles } from "../styles";
|
||||||
import "./ha-automation-condition-editor";
|
import "./ha-automation-condition-editor";
|
||||||
|
import type HaAutomationConditionEditor from "./ha-automation-condition-editor";
|
||||||
|
import "./types/ha-automation-condition-and";
|
||||||
|
import "./types/ha-automation-condition-device";
|
||||||
|
import "./types/ha-automation-condition-not";
|
||||||
|
import "./types/ha-automation-condition-numeric_state";
|
||||||
|
import "./types/ha-automation-condition-or";
|
||||||
|
import "./types/ha-automation-condition-state";
|
||||||
|
import "./types/ha-automation-condition-sun";
|
||||||
|
import "./types/ha-automation-condition-template";
|
||||||
|
import "./types/ha-automation-condition-time";
|
||||||
|
import "./types/ha-automation-condition-trigger";
|
||||||
|
import "./types/ha-automation-condition-zone";
|
||||||
|
|
||||||
export interface ConditionElement extends LitElement {
|
export interface ConditionElement extends LitElement {
|
||||||
condition: Condition;
|
condition: Condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
const preventDefault = (ev) => ev.preventDefault();
|
|
||||||
|
|
||||||
export const handleChangeEvent = (
|
export const handleChangeEvent = (
|
||||||
element: ConditionElement,
|
element: ConditionElement,
|
||||||
ev: CustomEvent
|
ev: CustomEvent
|
||||||
@@ -91,6 +108,15 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public last?: boolean;
|
@property({ type: Boolean }) public last?: boolean;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@state() private _collapsed = false;
|
||||||
|
|
||||||
|
@state() private _warnings?: string[];
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" })
|
||||||
|
public optionsInSidebar = false;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "automationClipboard",
|
key: "automationClipboard",
|
||||||
state: false,
|
state: false,
|
||||||
@@ -101,23 +127,202 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
|
|
||||||
@state() private _yamlMode = false;
|
@state() private _yamlMode = false;
|
||||||
|
|
||||||
@state() private _warnings?: string[];
|
|
||||||
|
|
||||||
@state() private _testing = false;
|
@state() private _testing = false;
|
||||||
|
|
||||||
@state() private _testingResult?: boolean;
|
@state() private _testingResult?: boolean;
|
||||||
|
|
||||||
|
@state() private _selected = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entityReg!: EntityRegistryEntry[];
|
_entityReg!: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
@query("ha-automation-condition-editor")
|
||||||
|
public conditionEditor?: HaAutomationConditionEditor;
|
||||||
|
|
||||||
|
private _renderRow() {
|
||||||
|
return html`
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="leading-icon"
|
||||||
|
class="condition-icon"
|
||||||
|
.path=${CONDITION_ICONS[this.condition.condition]}
|
||||||
|
></ha-svg-icon>
|
||||||
|
<h3 slot="header">
|
||||||
|
${capitalizeFirstLetter(
|
||||||
|
describeCondition(this.condition, this.hass, this._entityReg)
|
||||||
|
)}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<slot name="icons" slot="icons"></slot>
|
||||||
|
|
||||||
|
<ha-md-button-menu
|
||||||
|
slot="icons"
|
||||||
|
@click=${preventDefaultStopPropagation}
|
||||||
|
@keydown=${stopPropagation}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
positioning="fixed"
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
.path=${mdiDotsVertical}
|
||||||
|
>
|
||||||
|
</ha-icon-button>
|
||||||
|
|
||||||
|
<ha-md-menu-item .clickAction=${this._testCondition}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.conditions.test"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiFlask}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
${!this.optionsInSidebar
|
||||||
|
? html`
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._renameCondition}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.conditions.rename"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
|
||||||
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._duplicateCondition}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.duplicate"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentDuplicate}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._copyCondition}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.copy"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._cutCondition}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.cut"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._moveUp}
|
||||||
|
.disabled=${this.disabled || this.first}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||||
|
></ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._moveDown}
|
||||||
|
.disabled=${this.disabled || this.last}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.panel.config.automation.editor.move_down")}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||||
|
></ha-md-menu-item>
|
||||||
|
|
||||||
|
${!this.optionsInSidebar
|
||||||
|
? html`<ha-md-menu-item
|
||||||
|
.clickAction=${this._toggleYamlMode}
|
||||||
|
.disabled=${this._uiSupported(this.condition.condition) ||
|
||||||
|
!!this._warnings}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>`
|
||||||
|
: nothing}
|
||||||
|
|
||||||
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._onDisable}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.condition.enabled === false
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.enable"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.disable"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${this.condition.enabled === false
|
||||||
|
? mdiPlayCircleOutline
|
||||||
|
: mdiStopCircleOutline}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
<ha-md-menu-item
|
||||||
|
class="warning"
|
||||||
|
.clickAction=${this._onDelete}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class="warning"
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiDelete}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
</ha-md-button-menu>
|
||||||
|
|
||||||
|
${!this.optionsInSidebar
|
||||||
|
? html`${this._warnings
|
||||||
|
? html`<ha-automation-editor-warning
|
||||||
|
.localize=${this.hass.localize}
|
||||||
|
.warnings=${this._warnings}
|
||||||
|
>
|
||||||
|
</ha-automation-editor-warning>`
|
||||||
|
: nothing}
|
||||||
|
<ha-automation-condition-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.condition=${this.condition}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.yamlMode=${this._yamlMode}
|
||||||
|
.uiSupported=${this._uiSupported(this.condition.condition)}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
||||||
|
></ha-automation-condition-editor>`
|
||||||
|
: nothing}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.condition) {
|
if (!this.condition) {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card outlined>
|
<ha-card
|
||||||
|
outlined
|
||||||
|
class=${classMap({
|
||||||
|
selected: this._selected,
|
||||||
|
"building-block":
|
||||||
|
this.optionsInSidebar &&
|
||||||
|
CONDITION_BUILDING_BLOCKS.includes(this.condition.condition) &&
|
||||||
|
!this._collapsed,
|
||||||
|
})}
|
||||||
|
>
|
||||||
${this.condition.enabled === false
|
${this.condition.enabled === false
|
||||||
? html`
|
? html`
|
||||||
<div class="disabled-bar">
|
<div class="disabled-bar">
|
||||||
@@ -126,187 +331,27 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: nothing}
|
||||||
|
${this.optionsInSidebar
|
||||||
<ha-expansion-panel left-chevron>
|
? html`<ha-automation-row
|
||||||
<ha-svg-icon
|
.disabled=${this.condition.enabled === false}
|
||||||
slot="leading-icon"
|
.leftChevron=${CONDITION_BUILDING_BLOCKS.includes(
|
||||||
class="condition-icon"
|
this.condition.condition
|
||||||
.path=${CONDITION_ICONS[this.condition.condition]}
|
|
||||||
></ha-svg-icon>
|
|
||||||
<h3 slot="header">
|
|
||||||
${capitalizeFirstLetter(
|
|
||||||
describeCondition(this.condition, this.hass, this._entityReg)
|
|
||||||
)}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<slot name="icons" slot="icons"></slot>
|
|
||||||
|
|
||||||
<ha-md-button-menu
|
|
||||||
slot="icons"
|
|
||||||
@click=${preventDefault}
|
|
||||||
@keydown=${stopPropagation}
|
|
||||||
@closed=${stopPropagation}
|
|
||||||
positioning="fixed"
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
slot="trigger"
|
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
|
||||||
.path=${mdiDotsVertical}
|
|
||||||
>
|
|
||||||
</ha-icon-button>
|
|
||||||
|
|
||||||
<ha-md-menu-item .clickAction=${this._testCondition}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.conditions.test"
|
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="start" .path=${mdiFlask}></ha-svg-icon>
|
.collapsed=${this._collapsed}
|
||||||
</ha-md-menu-item>
|
.selected=${this._selected}
|
||||||
<ha-md-menu-item
|
@click=${this._toggleSidebar}
|
||||||
.clickAction=${this._renameCondition}
|
@toggle-collapsed=${this._toggleCollapse}
|
||||||
.disabled=${this.disabled}
|
.buildingBlock=${CONDITION_BUILDING_BLOCKS.includes(
|
||||||
>
|
this.condition.condition
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.conditions.rename"
|
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
>${this._renderRow()}</ha-automation-row
|
||||||
</ha-md-menu-item>
|
>`
|
||||||
|
: html`
|
||||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
<ha-expansion-panel left-chevron>
|
||||||
|
${this._renderRow()}
|
||||||
<ha-md-menu-item
|
</ha-expansion-panel>
|
||||||
.clickAction=${this._duplicateCondition}
|
`}
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.duplicate"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="start"
|
|
||||||
.path=${mdiContentDuplicate}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._copyCondition}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.copy"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._cutCondition}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.cut"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._moveUp}
|
|
||||||
.disabled=${this.disabled || this.first}
|
|
||||||
>
|
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
|
||||||
></ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._moveDown}
|
|
||||||
.disabled=${this.disabled || this.last}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.move_down"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
|
||||||
></ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._toggleYamlMode}
|
|
||||||
.disabled=${this._warnings}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._onDisable}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.condition.enabled === false
|
|
||||||
? this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.enable"
|
|
||||||
)
|
|
||||||
: this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.disable"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="start"
|
|
||||||
.path=${this.condition.enabled === false
|
|
||||||
? mdiPlayCircleOutline
|
|
||||||
: mdiStopCircleOutline}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
<ha-md-menu-item
|
|
||||||
class="warning"
|
|
||||||
.clickAction=${this._onDelete}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
class="warning"
|
|
||||||
slot="start"
|
|
||||||
.path=${mdiDelete}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
</ha-md-button-menu>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class=${classMap({
|
|
||||||
"card-content": true,
|
|
||||||
disabled: this.condition.enabled === false,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
${this._warnings
|
|
||||||
? html`<ha-alert
|
|
||||||
alert-type="warning"
|
|
||||||
.title=${this.hass.localize(
|
|
||||||
"ui.errors.config.editor_not_supported"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
${this._warnings!.length > 0 &&
|
|
||||||
this._warnings![0] !== undefined
|
|
||||||
? html` <ul>
|
|
||||||
${this._warnings!.map(
|
|
||||||
(warning) => html`<li>${warning}</li>`
|
|
||||||
)}
|
|
||||||
</ul>`
|
|
||||||
: ""}
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.errors.config.edit_in_yaml_supported"
|
|
||||||
)}
|
|
||||||
</ha-alert>`
|
|
||||||
: ""}
|
|
||||||
<ha-automation-condition-editor
|
|
||||||
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
|
||||||
@value-changed=${this._handleChangeEvent}
|
|
||||||
.yamlMode=${this._yamlMode}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
.hass=${this.hass}
|
|
||||||
.condition=${this.condition}
|
|
||||||
></ha-automation-condition-editor>
|
|
||||||
</div>
|
|
||||||
</ha-expansion-panel>
|
|
||||||
<div
|
<div
|
||||||
class="testing ${classMap({
|
class="testing ${classMap({
|
||||||
active: this._testing,
|
active: this._testing,
|
||||||
@@ -323,21 +368,35 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
|
||||||
|
${this.optionsInSidebar &&
|
||||||
|
CONDITION_BUILDING_BLOCKS.includes(this.condition.condition) &&
|
||||||
|
!this._collapsed
|
||||||
|
? html`<ha-automation-condition-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.condition=${this.condition}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.uiSupported=${this._uiSupported(this.condition.condition)}
|
||||||
|
indent
|
||||||
|
.selected=${this._selected}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@value-changed=${this._onValueChange}
|
||||||
|
></ha-automation-condition-editor>`
|
||||||
|
: nothing}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleUiModeNotAvailable(ev: CustomEvent) {
|
protected willUpdate(changedProperties) {
|
||||||
// Prevent possible parent action-row from switching to yamlMode
|
// on yaml toggle --> clear warnings
|
||||||
ev.stopPropagation();
|
if (changedProperties.has("yamlMode")) {
|
||||||
this._warnings = handleStructError(this.hass, ev.detail).warnings;
|
this._warnings = undefined;
|
||||||
if (!this._yamlMode) {
|
|
||||||
this._yamlMode = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleChangeEvent(ev: CustomEvent) {
|
private _onValueChange(event: CustomEvent) {
|
||||||
if (ev.detail.yaml) {
|
// reload sidebar if sort, deleted,... happend
|
||||||
this._warnings = undefined;
|
if (this._selected && this.optionsInSidebar) {
|
||||||
|
this.openSidebar(event.detail.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,6 +411,11 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
const enabled = !(this.condition.enabled ?? true);
|
const enabled = !(this.condition.enabled ?? true);
|
||||||
const value = { ...this.condition, enabled };
|
const value = { ...this.condition, enabled };
|
||||||
fireEvent(this, "value-changed", { value });
|
fireEvent(this, "value-changed", { value });
|
||||||
|
this.openSidebar(value); // refresh sidebar
|
||||||
|
|
||||||
|
if (this._yamlMode && !this.optionsInSidebar) {
|
||||||
|
this.conditionEditor?.yamlEditor?.setValue(value);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _onDelete = () => {
|
private _onDelete = () => {
|
||||||
@@ -367,17 +431,18 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
destructive: true,
|
destructive: true,
|
||||||
confirm: () => {
|
confirm: () => {
|
||||||
fireEvent(this, "value-changed", { value: null });
|
fireEvent(this, "value-changed", { value: null });
|
||||||
|
if (this._selected) {
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private _switchUiMode() {
|
private _switchUiMode() {
|
||||||
this._warnings = undefined;
|
|
||||||
this._yamlMode = false;
|
this._yamlMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _switchYamlMode() {
|
private _switchYamlMode() {
|
||||||
this._warnings = undefined;
|
|
||||||
this._yamlMode = true;
|
this._yamlMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,6 +528,12 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this._selected && this.optionsInSidebar) {
|
||||||
|
this.openSidebar(value); // refresh sidebar
|
||||||
|
} else if (this._yamlMode) {
|
||||||
|
this.conditionEditor?.yamlEditor?.setValue(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -477,6 +548,9 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
private _cutCondition = () => {
|
private _cutCondition = () => {
|
||||||
this._setClipboard();
|
this._setClipboard();
|
||||||
fireEvent(this, "value-changed", { value: null });
|
fireEvent(this, "value-changed", { value: null });
|
||||||
|
if (this._selected) {
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _moveUp = () => {
|
private _moveUp = () => {
|
||||||
@@ -493,7 +567,10 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
} else {
|
} else {
|
||||||
this._switchYamlMode();
|
this._switchYamlMode();
|
||||||
}
|
}
|
||||||
this.expand();
|
|
||||||
|
if (!this.optionsInSidebar) {
|
||||||
|
this.expand();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public expand() {
|
public expand() {
|
||||||
@@ -502,52 +579,68 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleUiModeNotAvailable(ev: CustomEvent) {
|
||||||
|
this._warnings = handleStructError(this.hass, ev.detail).warnings;
|
||||||
|
if (!this._yamlMode) {
|
||||||
|
this._yamlMode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleSidebar(ev: Event) {
|
||||||
|
ev?.stopPropagation();
|
||||||
|
|
||||||
|
if (this._selected) {
|
||||||
|
this._selected = false;
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openSidebar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public openSidebar(condition?: Condition): void {
|
||||||
|
if (this.narrow) {
|
||||||
|
this.scrollIntoView();
|
||||||
|
}
|
||||||
|
|
||||||
|
const sidebarCondition = condition || this.condition;
|
||||||
|
fireEvent(this, "open-sidebar", {
|
||||||
|
save: (value) => {
|
||||||
|
fireEvent(this, "value-changed", { value });
|
||||||
|
},
|
||||||
|
close: () => {
|
||||||
|
this._selected = false;
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
},
|
||||||
|
rename: () => {
|
||||||
|
this._renameCondition();
|
||||||
|
},
|
||||||
|
toggleYamlMode: () => {
|
||||||
|
this._toggleYamlMode();
|
||||||
|
return this._yamlMode;
|
||||||
|
},
|
||||||
|
disable: this._onDisable,
|
||||||
|
delete: this._onDelete,
|
||||||
|
config: sidebarCondition,
|
||||||
|
type: "condition",
|
||||||
|
uiSupported: this._uiSupported(sidebarCondition.condition),
|
||||||
|
yamlMode: this._yamlMode,
|
||||||
|
});
|
||||||
|
this._selected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _uiSupported = memoizeOne(
|
||||||
|
(type: string) =>
|
||||||
|
customElements.get(`ha-automation-condition-${type}`) !== undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
private _toggleCollapse() {
|
||||||
|
this._collapsed = !this._collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
rowStyles,
|
||||||
css`
|
css`
|
||||||
.disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
ha-expansion-panel {
|
|
||||||
--expansion-panel-summary-padding: 0 0 0 8px;
|
|
||||||
--expansion-panel-content-padding: 0;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
.condition-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
@media (min-width: 870px) {
|
|
||||||
.condition-icon {
|
|
||||||
display: inline-block;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.card-content {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
.disabled-bar {
|
|
||||||
background: var(--divider-color, #e0e0e0);
|
|
||||||
text-align: center;
|
|
||||||
border-top-right-radius: calc(
|
|
||||||
var(--ha-card-border-radius, 12px) - var(
|
|
||||||
--ha-card-border-width,
|
|
||||||
1px
|
|
||||||
)
|
|
||||||
);
|
|
||||||
border-top-left-radius: calc(
|
|
||||||
var(--ha-card-border-radius, 12px) - var(
|
|
||||||
--ha-card-border-width,
|
|
||||||
1px
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
.testing {
|
.testing {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
@@ -562,17 +655,8 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: max-height 0.3s;
|
transition: max-height 0.3s;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-top-right-radius: calc(
|
border-top-right-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
||||||
var(--ha-card-border-radius, 12px) - var(
|
border-top-left-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
||||||
--ha-card-border-width,
|
|
||||||
1px
|
|
||||||
)
|
|
||||||
);
|
|
||||||
border-top-left-radius: calc(
|
|
||||||
var(--ha-card-border-radius, 12px) - var(
|
|
||||||
--ha-card-border-width,
|
|
||||||
1px
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
.testing.active {
|
.testing.active {
|
||||||
@@ -584,15 +668,6 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
.testing.pass {
|
.testing.pass {
|
||||||
background-color: var(--success-color);
|
background-color: var(--success-color);
|
||||||
}
|
}
|
||||||
ha-md-menu-item > ha-svg-icon {
|
|
||||||
--mdc-icon-size: 24px;
|
|
||||||
}
|
|
||||||
:host([highlight]) ha-card {
|
|
||||||
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
|
|
||||||
--shadow-focus: 0 0 0 1px var(--state-inactive-color);
|
|
||||||
border-color: var(--state-inactive-color);
|
|
||||||
box-shadow: var(--shadow-default), var(--shadow-focus);
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
} from "../show-add-automation-element-dialog";
|
} from "../show-add-automation-element-dialog";
|
||||||
import "./ha-automation-condition-row";
|
import "./ha-automation-condition-row";
|
||||||
import type HaAutomationConditionRow from "./ha-automation-condition-row";
|
import type HaAutomationConditionRow from "./ha-automation-condition-row";
|
||||||
|
import { CONDITION_BUILDING_BLOCKS } from "../../../../data/condition";
|
||||||
|
|
||||||
@customElement("ha-automation-condition")
|
@customElement("ha-automation-condition")
|
||||||
export default class HaAutomationCondition extends LitElement {
|
export default class HaAutomationCondition extends LitElement {
|
||||||
@@ -34,8 +35,13 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public root = false;
|
@property({ type: Boolean }) public root = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" }) public optionsInSidebar =
|
||||||
|
false;
|
||||||
|
|
||||||
@state() private _showReorder = false;
|
@state() private _showReorder = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
@@ -96,7 +102,15 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
"ha-automation-condition-row:last-of-type"
|
"ha-automation-condition-row:last-of-type"
|
||||||
)!;
|
)!;
|
||||||
row.updateComplete.then(() => {
|
row.updateComplete.then(() => {
|
||||||
row.expand();
|
// on new condition open the settings in the sidebar, except for building blocks
|
||||||
|
if (
|
||||||
|
this.optionsInSidebar &&
|
||||||
|
!CONDITION_BUILDING_BLOCKS.includes(row.condition.condition)
|
||||||
|
) {
|
||||||
|
row.openSidebar();
|
||||||
|
} else if (!this.optionsInSidebar) {
|
||||||
|
row.expand();
|
||||||
|
}
|
||||||
row.scrollIntoView();
|
row.scrollIntoView();
|
||||||
row.focus();
|
row.focus();
|
||||||
});
|
});
|
||||||
@@ -140,12 +154,14 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
.totalConditions=${this.conditions.length}
|
.totalConditions=${this.conditions.length}
|
||||||
.condition=${cond}
|
.condition=${cond}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
|
.narrow=${this.narrow}
|
||||||
@duplicate=${this._duplicateCondition}
|
@duplicate=${this._duplicateCondition}
|
||||||
@move-down=${this._moveDown}
|
@move-down=${this._moveDown}
|
||||||
@move-up=${this._moveUp}
|
@move-up=${this._moveUp}
|
||||||
@value-changed=${this._conditionChanged}
|
@value-changed=${this._conditionChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
?highlight=${this.highlightedConditions?.includes(cond)}
|
?highlight=${this.highlightedConditions?.includes(cond)}
|
||||||
|
.optionsInSidebar=${this.optionsInSidebar}
|
||||||
>
|
>
|
||||||
${this._showReorder && !this.disabled
|
${this._showReorder && !this.disabled
|
||||||
? html`
|
? html`
|
||||||
@@ -292,6 +308,7 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
// Ensure condition is removed even after update
|
// Ensure condition is removed even after update
|
||||||
const conditions = this.conditions.filter((c) => c !== condition);
|
const conditions = this.conditions.filter((c) => c !== condition);
|
||||||
fireEvent(this, "value-changed", { value: conditions });
|
fireEvent(this, "value-changed", { value: conditions });
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
}
|
}
|
||||||
|
|
||||||
private _conditionChanged(ev: CustomEvent) {
|
private _conditionChanged(ev: CustomEvent) {
|
||||||
@@ -325,15 +342,18 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
.conditions {
|
.conditions {
|
||||||
padding: 16px;
|
padding: 16px 0 16px 16px;
|
||||||
margin: -16px;
|
margin: -16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
:host([root]) .conditions {
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
.sortable-ghost {
|
.sortable-ghost {
|
||||||
background: none;
|
background: none;
|
||||||
border-radius: var(--ha-card-border-radius, 12px);
|
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
||||||
}
|
}
|
||||||
.sortable-drag {
|
.sortable-drag {
|
||||||
background: none;
|
background: none;
|
||||||
@@ -342,9 +362,6 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
display: block;
|
display: block;
|
||||||
scroll-margin-top: 48px;
|
scroll-margin-top: 48px;
|
||||||
}
|
}
|
||||||
.buttons {
|
|
||||||
order: 1;
|
|
||||||
}
|
|
||||||
.handle {
|
.handle {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
cursor: move; /* fallback if grab cursor is unsupported */
|
cursor: move; /* fallback if grab cursor is unsupported */
|
||||||
@@ -358,6 +375,7 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
order: 1;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ export abstract class HaLogicalCondition
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" }) public optionsInSidebar =
|
||||||
|
false;
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<ha-automation-condition
|
<ha-automation-condition
|
||||||
@@ -24,6 +29,8 @@ export abstract class HaLogicalCondition
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
|
.optionsInSidebar=${this.optionsInSidebar}
|
||||||
|
.narrow=${this.narrow}
|
||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ class DialogNewAutomation extends LitElement implements HassDialog {
|
|||||||
ha-dialog {
|
ha-dialog {
|
||||||
--dialog-content-padding: 0;
|
--dialog-content-padding: 0;
|
||||||
--mdc-dialog-max-height: 60vh;
|
--mdc-dialog-max-height: 60vh;
|
||||||
|
--mdc-dialog-max-height: 60dvh;
|
||||||
}
|
}
|
||||||
@media all and (min-width: 550px) {
|
@media all and (min-width: 550px) {
|
||||||
ha-dialog {
|
ha-dialog {
|
||||||
|
|||||||
36
src/panels/config/automation/ha-automation-editor-warning.ts
Normal file
36
src/panels/config/automation/ha-automation-editor-warning.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
|
import "../../../components/ha-alert";
|
||||||
|
|
||||||
|
@customElement("ha-automation-editor-warning")
|
||||||
|
export class HaAutomationEditorWarning extends LitElement {
|
||||||
|
@property({ attribute: false }) public localize!: LocalizeFunc;
|
||||||
|
|
||||||
|
@property({ attribute: "alert-title" }) public alertTitle?: string;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public warnings: string[] = [];
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<ha-alert
|
||||||
|
alert-type="warning"
|
||||||
|
.title=${this.alertTitle ||
|
||||||
|
this.localize("ui.errors.config.editor_not_supported")}
|
||||||
|
>
|
||||||
|
${this.warnings.length && this.warnings[0] !== undefined
|
||||||
|
? html`<ul>
|
||||||
|
${this.warnings.map((warning) => html`<li>${warning}</li>`)}
|
||||||
|
</ul>`
|
||||||
|
: nothing}
|
||||||
|
${this.localize("ui.errors.config.edit_in_yaml_supported")}
|
||||||
|
</ha-alert>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-editor-warning": HaAutomationEditorWarning;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,9 +28,9 @@ import { navigate } from "../../../common/navigate";
|
|||||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||||
import { promiseTimeout } from "../../../common/util/promise-timeout";
|
import { promiseTimeout } from "../../../common/util/promise-timeout";
|
||||||
import { afterNextRender } from "../../../common/util/render-status";
|
import { afterNextRender } from "../../../common/util/render-status";
|
||||||
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-button-menu";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-fab";
|
||||||
import "../../../components/ha-button";
|
|
||||||
import "../../../components/ha-fade-in";
|
import "../../../components/ha-fade-in";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
@@ -97,6 +97,7 @@ declare global {
|
|||||||
"move-down": undefined;
|
"move-down": undefined;
|
||||||
"move-up": undefined;
|
"move-up": undefined;
|
||||||
duplicate: undefined;
|
duplicate: undefined;
|
||||||
|
"save-automation": undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,61 +404,65 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
<div
|
<div
|
||||||
class="content ${classMap({
|
class=${this._mode === "yaml" ? "yaml-mode" : ""}
|
||||||
"yaml-mode": this._mode === "yaml",
|
|
||||||
})}"
|
|
||||||
@subscribe-automation-config=${this._subscribeAutomationConfig}
|
@subscribe-automation-config=${this._subscribeAutomationConfig}
|
||||||
>
|
>
|
||||||
${this._errors || stateObj?.state === UNAVAILABLE
|
<div class="error-wrapper">
|
||||||
? html`<ha-alert
|
${this._errors || stateObj?.state === UNAVAILABLE
|
||||||
alert-type="error"
|
? html`<ha-alert
|
||||||
.title=${stateObj?.state === UNAVAILABLE
|
alert-type="error"
|
||||||
? this.hass.localize(
|
.title=${stateObj?.state === UNAVAILABLE
|
||||||
"ui.panel.config.automation.editor.unavailable"
|
? this.hass.localize(
|
||||||
)
|
"ui.panel.config.automation.editor.unavailable"
|
||||||
: undefined}
|
)
|
||||||
>
|
: undefined}
|
||||||
${this._errors || this._validationErrors}
|
>
|
||||||
${stateObj?.state === UNAVAILABLE
|
${this._errors || this._validationErrors}
|
||||||
? html`<ha-svg-icon
|
${stateObj?.state === UNAVAILABLE
|
||||||
slot="icon"
|
? html`<ha-svg-icon
|
||||||
.path=${mdiRobotConfused}
|
slot="icon"
|
||||||
></ha-svg-icon>`
|
.path=${mdiRobotConfused}
|
||||||
: nothing}
|
></ha-svg-icon>`
|
||||||
</ha-alert>`
|
: nothing}
|
||||||
: ""}
|
|
||||||
${this._blueprintConfig
|
|
||||||
? html`<ha-alert alert-type="info">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.confirm_take_control"
|
|
||||||
)}
|
|
||||||
<div slot="action" style="display: flex;">
|
|
||||||
<ha-button appearance="plain" @click=${this._takeControlSave}
|
|
||||||
>${this.hass.localize("ui.common.yes")}</ha-button
|
|
||||||
>
|
|
||||||
<ha-button appearance="plain" @click=${this._revertBlueprint}
|
|
||||||
>${this.hass.localize("ui.common.no")}</ha-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</ha-alert>`
|
|
||||||
: this._readOnly
|
|
||||||
? html`<ha-alert alert-type="warning" dismissable
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.read_only"
|
|
||||||
)}
|
|
||||||
<ha-button
|
|
||||||
appearance="filled"
|
|
||||||
size="small"
|
|
||||||
variant="warning"
|
|
||||||
slot="action"
|
|
||||||
@click=${this._duplicate}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.migrate"
|
|
||||||
)}
|
|
||||||
</ha-button>
|
|
||||||
</ha-alert>`
|
</ha-alert>`
|
||||||
: nothing}
|
: ""}
|
||||||
|
${this._blueprintConfig
|
||||||
|
? html`<ha-alert alert-type="info">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.confirm_take_control"
|
||||||
|
)}
|
||||||
|
<div slot="action" style="display: flex;">
|
||||||
|
<ha-button
|
||||||
|
appearance="plain"
|
||||||
|
@click=${this._takeControlSave}
|
||||||
|
>${this.hass.localize("ui.common.yes")}</ha-button
|
||||||
|
>
|
||||||
|
<ha-button
|
||||||
|
appearance="plain"
|
||||||
|
@click=${this._revertBlueprint}
|
||||||
|
>${this.hass.localize("ui.common.no")}</ha-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</ha-alert>`
|
||||||
|
: this._readOnly
|
||||||
|
? html`<ha-alert alert-type="warning" dismissable
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.read_only"
|
||||||
|
)}
|
||||||
|
<ha-button
|
||||||
|
appearance="filled"
|
||||||
|
size="small"
|
||||||
|
variant="warning"
|
||||||
|
slot="action"
|
||||||
|
@click=${this._duplicate}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.migrate"
|
||||||
|
)}
|
||||||
|
</ha-button>
|
||||||
|
</ha-alert>`
|
||||||
|
: nothing}
|
||||||
|
</div>
|
||||||
${this._mode === "gui"
|
${this._mode === "gui"
|
||||||
? html`
|
? html`
|
||||||
<div
|
<div
|
||||||
@@ -474,7 +479,10 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.config=${this._config}
|
.config=${this._config}
|
||||||
.disabled=${Boolean(this._readOnly)}
|
.disabled=${Boolean(this._readOnly)}
|
||||||
|
.saving=${this._saving}
|
||||||
|
.dirty=${this._dirty}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
|
@save-automation=${this._handleSaveAutomation}
|
||||||
></blueprint-automation-editor>
|
></blueprint-automation-editor>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
@@ -486,7 +494,9 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
.config=${this._config}
|
.config=${this._config}
|
||||||
.disabled=${Boolean(this._readOnly)}
|
.disabled=${Boolean(this._readOnly)}
|
||||||
.dirty=${this._dirty}
|
.dirty=${this._dirty}
|
||||||
|
.saving=${this._saving}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
|
@save-automation=${this._handleSaveAutomation}
|
||||||
@editor-save=${this._handleSaveAutomation}
|
@editor-save=${this._handleSaveAutomation}
|
||||||
></manual-automation-editor>
|
></manual-automation-editor>
|
||||||
`}
|
`}
|
||||||
@@ -521,21 +531,24 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
@editor-save=${this._handleSaveAutomation}
|
@editor-save=${this._handleSaveAutomation}
|
||||||
.showErrors=${false}
|
.showErrors=${false}
|
||||||
disable-fullscreen
|
disable-fullscreen
|
||||||
></ha-yaml-editor>`
|
></ha-yaml-editor>
|
||||||
|
<ha-fab
|
||||||
|
slot="fab"
|
||||||
|
class=${this._dirty ? "dirty" : ""}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.save"
|
||||||
|
)}
|
||||||
|
.disabled=${this._saving}
|
||||||
|
extended
|
||||||
|
@click=${this._saveAutomation}
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="icon"
|
||||||
|
.path=${mdiContentSave}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-fab>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
<ha-fab
|
|
||||||
slot="fab"
|
|
||||||
class=${classMap({
|
|
||||||
dirty: !this._readOnly && this._dirty,
|
|
||||||
})}
|
|
||||||
.label=${this.hass.localize("ui.panel.config.automation.editor.save")}
|
|
||||||
.disabled=${this._saving}
|
|
||||||
extended
|
|
||||||
@click=${this._handleSaveAutomation}
|
|
||||||
>
|
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
|
||||||
</ha-fab>
|
|
||||||
</hass-subpage>
|
</hass-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -1102,9 +1115,6 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.content {
|
|
||||||
padding-bottom: 20px;
|
|
||||||
}
|
|
||||||
.yaml-mode {
|
.yaml-mode {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1112,13 +1122,34 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
manual-automation-editor,
|
manual-automation-editor,
|
||||||
blueprint-automation-editor,
|
blueprint-automation-editor {
|
||||||
:not(.yaml-mode) > ha-alert {
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 1040px;
|
max-width: 1040px;
|
||||||
padding: 28px 20px 0;
|
padding: 28px 20px 0;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:not(.yaml-mode) > .error-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
z-index: 3;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(.yaml-mode) > .error-wrapper ha-alert {
|
||||||
|
background-color: var(--card-background-color);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: var(--ha-border-radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
manual-automation-editor {
|
||||||
|
max-width: 1540px;
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
ha-yaml-editor {
|
ha-yaml-editor {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
--actions-border-radius: 0;
|
--actions-border-radius: 0;
|
||||||
@@ -1135,14 +1166,6 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
margin-inline-end: 8px;
|
margin-inline-end: 8px;
|
||||||
margin-inline-start: initial;
|
margin-inline-start: initial;
|
||||||
}
|
}
|
||||||
ha-fab {
|
|
||||||
position: relative;
|
|
||||||
bottom: calc(-80px - var(--safe-area-inset-bottom));
|
|
||||||
transition: bottom 0.3s;
|
|
||||||
}
|
|
||||||
ha-fab.dirty {
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
li[role="separator"] {
|
li[role="separator"] {
|
||||||
border-bottom-color: var(--divider-color);
|
border-bottom-color: var(--divider-color);
|
||||||
}
|
}
|
||||||
@@ -1160,6 +1183,15 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
max-width: 1040px;
|
max-width: 1040px;
|
||||||
padding: 28px 20px 0;
|
padding: 28px 20px 0;
|
||||||
}
|
}
|
||||||
|
ha-fab {
|
||||||
|
position: fixed;
|
||||||
|
right: 16px;
|
||||||
|
bottom: calc(-80px - var(--safe-area-inset-bottom));
|
||||||
|
transition: bottom 0.3s;
|
||||||
|
}
|
||||||
|
ha-fab.dirty {
|
||||||
|
bottom: 16px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
412
src/panels/config/automation/ha-automation-sidebar.ts
Normal file
412
src/panels/config/automation/ha-automation-sidebar.ts
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
import {
|
||||||
|
mdiClose,
|
||||||
|
mdiDelete,
|
||||||
|
mdiDotsVertical,
|
||||||
|
mdiIdentifier,
|
||||||
|
mdiPlayCircleOutline,
|
||||||
|
mdiPlaylistEdit,
|
||||||
|
mdiRenameBox,
|
||||||
|
mdiStopCircleOutline,
|
||||||
|
} from "@mdi/js";
|
||||||
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
|
import { handleStructError } from "../../../common/structs/handle-errors";
|
||||||
|
import type { LocalizeKeys } from "../../../common/translations/localize";
|
||||||
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-dialog-header";
|
||||||
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-md-button-menu";
|
||||||
|
import "../../../components/ha-md-divider";
|
||||||
|
import "../../../components/ha-md-menu-item";
|
||||||
|
import type { Condition, Trigger } from "../../../data/automation";
|
||||||
|
import type { Action, RepeatAction } from "../../../data/script";
|
||||||
|
import { isTriggerList } from "../../../data/trigger";
|
||||||
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
import "./action/ha-automation-action-editor";
|
||||||
|
import { getAutomationActionType } from "./action/ha-automation-action-row";
|
||||||
|
import { getRepeatType } from "./action/types/ha-automation-action-repeat";
|
||||||
|
import "./condition/ha-automation-condition-editor";
|
||||||
|
import type HaAutomationConditionEditor from "./condition/ha-automation-condition-editor";
|
||||||
|
import "./ha-automation-editor-warning";
|
||||||
|
import "./trigger/ha-automation-trigger-editor";
|
||||||
|
import type HaAutomationTriggerEditor from "./trigger/ha-automation-trigger-editor";
|
||||||
|
import { ACTION_BUILDING_BLOCKS } from "../../../data/action";
|
||||||
|
import { CONDITION_BUILDING_BLOCKS } from "../../../data/condition";
|
||||||
|
|
||||||
|
export interface OpenSidebarConfig {
|
||||||
|
save: (config: Trigger | Condition | Action) => void;
|
||||||
|
close: () => void;
|
||||||
|
rename: () => void;
|
||||||
|
toggleYamlMode: () => boolean;
|
||||||
|
disable: () => void;
|
||||||
|
delete: () => void;
|
||||||
|
config: Trigger | Condition | Action;
|
||||||
|
type: "trigger" | "condition" | "action" | "option";
|
||||||
|
uiSupported: boolean;
|
||||||
|
yamlMode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("ha-automation-sidebar")
|
||||||
|
export default class HaAutomationSidebar extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public config?: OpenSidebarConfig;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "wide" }) public isWide = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@state() private _yamlMode = false;
|
||||||
|
|
||||||
|
@state() private _requestShowId = false;
|
||||||
|
|
||||||
|
@state() private _warnings?: string[];
|
||||||
|
|
||||||
|
@query(".sidebar-editor")
|
||||||
|
public editor?: HaAutomationTriggerEditor | HaAutomationConditionEditor;
|
||||||
|
|
||||||
|
protected willUpdate(changedProperties) {
|
||||||
|
if (changedProperties.has("config")) {
|
||||||
|
this._requestShowId = false;
|
||||||
|
this._warnings = undefined;
|
||||||
|
if (this.config) {
|
||||||
|
this._yamlMode = this.config.yamlMode;
|
||||||
|
if (this._yamlMode) {
|
||||||
|
this.editor?.yamlEditor?.setValue(this.config.config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this.config) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const disabled =
|
||||||
|
this.disabled ||
|
||||||
|
("enabled" in this.config.config && this.config.config.enabled === false);
|
||||||
|
let type = isTriggerList(this.config.config as Trigger)
|
||||||
|
? "list"
|
||||||
|
: this.config.type === "action"
|
||||||
|
? getAutomationActionType(this.config.config as Action)
|
||||||
|
: this.config.config[this.config.type];
|
||||||
|
|
||||||
|
if (this.config.type === "action" && type === "repeat") {
|
||||||
|
type = `repeat_${getRepeatType((this.config.config as RepeatAction).repeat)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isBuildingBlock = [
|
||||||
|
...CONDITION_BUILDING_BLOCKS,
|
||||||
|
...ACTION_BUILDING_BLOCKS,
|
||||||
|
].includes(type);
|
||||||
|
|
||||||
|
const subtitle = this.hass.localize(
|
||||||
|
(this.config.type === "option"
|
||||||
|
? "ui.panel.config.automation.editor.actions.type.choose.label"
|
||||||
|
: `ui.panel.config.automation.editor.${this.config.type}s.${this.config.type}`) as LocalizeKeys
|
||||||
|
);
|
||||||
|
const title =
|
||||||
|
this.hass.localize(
|
||||||
|
(this.config.type === "option"
|
||||||
|
? "ui.panel.config.automation.editor.actions.type.choose.option_label"
|
||||||
|
: `ui.panel.config.automation.editor.${this.config.type}s.type.${type}.label`) as LocalizeKeys
|
||||||
|
) || type;
|
||||||
|
|
||||||
|
const description =
|
||||||
|
isBuildingBlock || this.config.type === "option"
|
||||||
|
? this.hass.localize(
|
||||||
|
(this.config.type === "option"
|
||||||
|
? "ui.panel.config.automation.editor.actions.type.choose.option_description"
|
||||||
|
: `ui.panel.config.automation.editor.${this.config.type}s.type.${type}.description.picker`) as LocalizeKeys
|
||||||
|
)
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-card
|
||||||
|
outlined
|
||||||
|
class=${classMap({
|
||||||
|
mobile: !this.isWide,
|
||||||
|
yaml: this._yamlMode,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<ha-dialog-header>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="navigationIcon"
|
||||||
|
.label=${this.hass.localize("ui.common.close")}
|
||||||
|
.path=${mdiClose}
|
||||||
|
@click=${this._closeSidebar}
|
||||||
|
></ha-icon-button>
|
||||||
|
<span slot="title">${title}</span>
|
||||||
|
<span slot="subtitle">${subtitle}</span>
|
||||||
|
<ha-md-button-menu
|
||||||
|
slot="actionItems"
|
||||||
|
@click=${this._openOverflowMenu}
|
||||||
|
@keydown=${stopPropagation}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
positioning="fixed"
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
.path=${mdiDotsVertical}
|
||||||
|
></ha-icon-button>
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this.config.rename}
|
||||||
|
.disabled=${disabled || type === "list"}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.rename"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
${this.config.type === "trigger" &&
|
||||||
|
!this._yamlMode &&
|
||||||
|
!("id" in this.config.config) &&
|
||||||
|
!this._requestShowId
|
||||||
|
? html`<ha-md-menu-item
|
||||||
|
.clickAction=${this._showTriggerId}
|
||||||
|
.disabled=${disabled || type === "list"}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.edit_id"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiIdentifier}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>`
|
||||||
|
: nothing}
|
||||||
|
${this.config.type !== "option"
|
||||||
|
? html`
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._toggleYamlMode}
|
||||||
|
.disabled=${!this.config.uiSupported || !!this._warnings}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiPlaylistEdit}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
|
||||||
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
|
${this.config.type !== "option"
|
||||||
|
? html`
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this.config.disable}
|
||||||
|
.disabled=${this.disabled || type === "list"}
|
||||||
|
>
|
||||||
|
${disabled
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.enable"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.disable"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${disabled
|
||||||
|
? mdiPlayCircleOutline
|
||||||
|
: mdiStopCircleOutline}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this.config.delete}
|
||||||
|
class="warning"
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.actions.${this.config.type !== "option" ? "delete" : "type.choose.remove_option"}`
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class="warning"
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiDelete}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
</ha-md-button-menu>
|
||||||
|
</ha-dialog-header>
|
||||||
|
${this._warnings
|
||||||
|
? html`<ha-automation-editor-warning
|
||||||
|
.localize=${this.hass.localize}
|
||||||
|
.warnings=${this._warnings}
|
||||||
|
>
|
||||||
|
</ha-automation-editor-warning>`
|
||||||
|
: nothing}
|
||||||
|
<div class="card-content">
|
||||||
|
${this.config.type === "trigger"
|
||||||
|
? html`<ha-automation-trigger-editor
|
||||||
|
class="sidebar-editor"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.trigger=${this.config.config as Trigger}
|
||||||
|
@value-changed=${this._valueChangedSidebar}
|
||||||
|
.uiSupported=${this.config.uiSupported}
|
||||||
|
.showId=${this._requestShowId}
|
||||||
|
.yamlMode=${this._yamlMode}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
||||||
|
></ha-automation-trigger-editor>`
|
||||||
|
: this.config.type === "condition" &&
|
||||||
|
(this._yamlMode || !CONDITION_BUILDING_BLOCKS.includes(type))
|
||||||
|
? html`
|
||||||
|
<ha-automation-condition-editor
|
||||||
|
class="sidebar-editor"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.condition=${this.config.config as Condition}
|
||||||
|
.yamlMode=${this._yamlMode}
|
||||||
|
.uiSupported=${this.config.uiSupported}
|
||||||
|
@value-changed=${this._valueChangedSidebar}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
||||||
|
></ha-automation-condition-editor>
|
||||||
|
`
|
||||||
|
: this.config.type === "action" &&
|
||||||
|
(this._yamlMode || !ACTION_BUILDING_BLOCKS.includes(type))
|
||||||
|
? html`
|
||||||
|
<ha-automation-action-editor
|
||||||
|
class="sidebar-editor"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.action=${this.config.config as Action}
|
||||||
|
.yamlMode=${this._yamlMode}
|
||||||
|
.uiSupported=${this.config.uiSupported}
|
||||||
|
@value-changed=${this._valueChangedSidebar}
|
||||||
|
sidebar
|
||||||
|
narrow
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
||||||
|
></ha-automation-action-editor>
|
||||||
|
`
|
||||||
|
: description || nothing}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleUiModeNotAvailable(ev: CustomEvent) {
|
||||||
|
this._warnings = handleStructError(this.hass, ev.detail).warnings;
|
||||||
|
if (!this._yamlMode) {
|
||||||
|
this._yamlMode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChangedSidebar(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
this.config?.save(ev.detail.value);
|
||||||
|
|
||||||
|
if (this.config) {
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.config,
|
||||||
|
config: ev.detail.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _closeSidebar() {
|
||||||
|
this.config?.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openOverflowMenu(ev: MouseEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleYamlMode = () => {
|
||||||
|
this._yamlMode = this.config!.toggleYamlMode();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.config,
|
||||||
|
yamlMode: this._yamlMode,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private _showTriggerId = () => {
|
||||||
|
this._requestShowId = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
height: 100%;
|
||||||
|
--ha-card-border-radius: var(
|
||||||
|
--ha-dialog-border-radius,
|
||||||
|
var(--ha-border-radius-2xl)
|
||||||
|
);
|
||||||
|
border-radius: var(--ha-card-border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-card {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
border-width: 2px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
ha-card.mobile {
|
||||||
|
border-bottom-right-radius: var(--ha-border-radius-square);
|
||||||
|
border-bottom-left-radius: var(--ha-border-radius-square);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 870px) {
|
||||||
|
ha-card.mobile {
|
||||||
|
max-height: 70vh;
|
||||||
|
max-height: 70dvh;
|
||||||
|
border-width: 2px 2px 0;
|
||||||
|
}
|
||||||
|
ha-card.mobile.yaml {
|
||||||
|
height: 70vh;
|
||||||
|
height: 70dvh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-dialog-header {
|
||||||
|
border-radius: var(--ha-card-border-radius);
|
||||||
|
}
|
||||||
|
.sidebar-editor {
|
||||||
|
padding-top: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
max-height: calc(100% - 80px);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 450px) and (min-height: 500px) {
|
||||||
|
.card-content {
|
||||||
|
max-height: calc(100% - 104px);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 870px) {
|
||||||
|
ha-card.mobile .card-content {
|
||||||
|
max-height: calc(
|
||||||
|
70vh - 88px - max(var(--safe-area-inset-bottom), 16px)
|
||||||
|
);
|
||||||
|
max-height: calc(
|
||||||
|
70dvh - 88px - max(var(--safe-area-inset-bottom), 16px)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-sidebar": HaAutomationSidebar;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { mdiHelpCircle } from "@mdi/js";
|
import { mdiContentSave, mdiHelpCircle } from "@mdi/js";
|
||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
import {
|
import {
|
||||||
any,
|
any,
|
||||||
array,
|
array,
|
||||||
@@ -23,7 +24,7 @@ import {
|
|||||||
removeSearchParam,
|
removeSearchParam,
|
||||||
} from "../../../common/url/search-params";
|
} from "../../../common/url/search-params";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-fab";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-markdown";
|
import "../../../components/ha-markdown";
|
||||||
import type {
|
import type {
|
||||||
@@ -38,7 +39,6 @@ import {
|
|||||||
normalizeAutomationConfig,
|
normalizeAutomationConfig,
|
||||||
} from "../../../data/automation";
|
} from "../../../data/automation";
|
||||||
import { getActionType, type Action } from "../../../data/script";
|
import { getActionType, type Action } from "../../../data/script";
|
||||||
import { haStyle } from "../../../resources/styles";
|
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import { showToast } from "../../../util/toast";
|
import { showToast } from "../../../util/toast";
|
||||||
@@ -46,7 +46,10 @@ import "./action/ha-automation-action";
|
|||||||
import type HaAutomationAction from "./action/ha-automation-action";
|
import type HaAutomationAction from "./action/ha-automation-action";
|
||||||
import "./condition/ha-automation-condition";
|
import "./condition/ha-automation-condition";
|
||||||
import type HaAutomationCondition from "./condition/ha-automation-condition";
|
import type HaAutomationCondition from "./condition/ha-automation-condition";
|
||||||
|
import "./ha-automation-sidebar";
|
||||||
|
import type { OpenSidebarConfig } from "./ha-automation-sidebar";
|
||||||
import { showPasteReplaceDialog } from "./paste-replace-dialog/show-dialog-paste-replace";
|
import { showPasteReplaceDialog } from "./paste-replace-dialog/show-dialog-paste-replace";
|
||||||
|
import { saveFabStyles } from "./styles";
|
||||||
import "./trigger/ha-automation-trigger";
|
import "./trigger/ha-automation-trigger";
|
||||||
import type HaAutomationTrigger from "./trigger/ha-automation-trigger";
|
import type HaAutomationTrigger from "./trigger/ha-automation-trigger";
|
||||||
|
|
||||||
@@ -77,6 +80,8 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public saving = false;
|
||||||
|
|
||||||
@property({ attribute: false }) public config!: ManualAutomationConfig;
|
@property({ attribute: false }) public config!: ManualAutomationConfig;
|
||||||
|
|
||||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||||
@@ -85,6 +90,8 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
|
|
||||||
@state() private _pastedConfig?: ManualAutomationConfig;
|
@state() private _pastedConfig?: ManualAutomationConfig;
|
||||||
|
|
||||||
|
@state() private _sidebarConfig?: OpenSidebarConfig;
|
||||||
|
|
||||||
private _previousConfig?: ManualAutomationConfig;
|
private _previousConfig?: ManualAutomationConfig;
|
||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
@@ -122,7 +129,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
private _renderContent() {
|
||||||
return html`
|
return html`
|
||||||
${this.stateObj?.state === "off"
|
${this.stateObj?.state === "off"
|
||||||
? html`
|
? html`
|
||||||
@@ -130,12 +137,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.disabled"
|
"ui.panel.config.automation.editor.disabled"
|
||||||
)}
|
)}
|
||||||
<ha-button
|
<ha-button size="small" slot="action" @click=${this._enable}>
|
||||||
size="small"
|
|
||||||
appearance="filled"
|
|
||||||
slot="action"
|
|
||||||
@click=${this._enable}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.enable"
|
"ui.panel.config.automation.editor.enable"
|
||||||
)}
|
)}
|
||||||
@@ -182,10 +184,14 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
aria-labelledby="triggers-heading"
|
aria-labelledby="triggers-heading"
|
||||||
.triggers=${this.config.triggers || []}
|
.triggers=${this.config.triggers || []}
|
||||||
.highlightedTriggers=${this._pastedConfig?.triggers || []}
|
.highlightedTriggers=${this._pastedConfig?.triggers || []}
|
||||||
.path=${["triggers"]}
|
|
||||||
@value-changed=${this._triggerChanged}
|
@value-changed=${this._triggerChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled || this.saving}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@open-sidebar=${this._openSidebar}
|
||||||
|
@close-sidebar=${this._handleCloseSidebar}
|
||||||
|
root
|
||||||
|
sidebar
|
||||||
></ha-automation-trigger>
|
></ha-automation-trigger>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
@@ -224,11 +230,14 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
aria-labelledby="conditions-heading"
|
aria-labelledby="conditions-heading"
|
||||||
.conditions=${this.config.conditions || []}
|
.conditions=${this.config.conditions || []}
|
||||||
.highlightedConditions=${this._pastedConfig?.conditions || []}
|
.highlightedConditions=${this._pastedConfig?.conditions || []}
|
||||||
.path=${["conditions"]}
|
|
||||||
@value-changed=${this._conditionChanged}
|
@value-changed=${this._conditionChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled || this.saving}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@open-sidebar=${this._openSidebar}
|
||||||
|
@close-sidebar=${this._handleCloseSidebar}
|
||||||
root
|
root
|
||||||
|
sidebar
|
||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
@@ -265,16 +274,82 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
aria-labelledby="actions-heading"
|
aria-labelledby="actions-heading"
|
||||||
.actions=${this.config.actions || []}
|
.actions=${this.config.actions || []}
|
||||||
.highlightedActions=${this._pastedConfig?.actions || []}
|
.highlightedActions=${this._pastedConfig?.actions || []}
|
||||||
.path=${["actions"]}
|
|
||||||
@value-changed=${this._actionChanged}
|
@value-changed=${this._actionChanged}
|
||||||
|
@open-sidebar=${this._openSidebar}
|
||||||
|
@close-sidebar=${this._handleCloseSidebar}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled || this.saving}
|
||||||
root
|
root
|
||||||
|
sidebar
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<div class="split-view">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<div class="content">${this._renderContent()}</div>
|
||||||
|
<ha-fab
|
||||||
|
slot="fab"
|
||||||
|
class=${this.dirty ? "dirty" : ""}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.save"
|
||||||
|
)}
|
||||||
|
.disabled=${this.saving}
|
||||||
|
extended
|
||||||
|
@click=${this._saveAutomation}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||||
|
</ha-fab>
|
||||||
|
</div>
|
||||||
|
<ha-automation-sidebar
|
||||||
|
class=${classMap({
|
||||||
|
sidebar: true,
|
||||||
|
hidden: !this._sidebarConfig,
|
||||||
|
overlay: !this.isWide,
|
||||||
|
})}
|
||||||
|
.isWide=${this.isWide}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.config=${this._sidebarConfig}
|
||||||
|
@value-changed=${this._sidebarConfigChanged}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
></ha-automation-sidebar>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openSidebar(ev: CustomEvent<OpenSidebarConfig>) {
|
||||||
|
// deselect previous selected row
|
||||||
|
this._sidebarConfig?.close?.();
|
||||||
|
this._sidebarConfig = ev.detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _sidebarConfigChanged(ev: CustomEvent<{ value: OpenSidebarConfig }>) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!this._sidebarConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._sidebarConfig = {
|
||||||
|
...this._sidebarConfig,
|
||||||
|
...ev.detail.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _closeSidebar() {
|
||||||
|
if (this._sidebarConfig) {
|
||||||
|
const closeRow = this._sidebarConfig?.close;
|
||||||
|
this._sidebarConfig = undefined;
|
||||||
|
closeRow?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleCloseSidebar() {
|
||||||
|
this._sidebarConfig = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
private _triggerChanged(ev: CustomEvent): void {
|
private _triggerChanged(ev: CustomEvent): void {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.resetPastedConfig();
|
this.resetPastedConfig();
|
||||||
@@ -311,6 +386,11 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _saveAutomation() {
|
||||||
|
this._closeSidebar();
|
||||||
|
fireEvent(this, "save-automation");
|
||||||
|
}
|
||||||
|
|
||||||
private _handlePaste = async (ev: ClipboardEvent) => {
|
private _handlePaste = async (ev: ClipboardEvent) => {
|
||||||
if (!canOverrideAlphanumericInput(ev.composedPath())) {
|
if (!canOverrideAlphanumericInput(ev.composedPath())) {
|
||||||
return;
|
return;
|
||||||
@@ -523,14 +603,77 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
saveFabStyles,
|
||||||
css`
|
css`
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
ha-card {
|
|
||||||
overflow: hidden;
|
.split-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
position: relative;
|
||||||
|
flex: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 32px 16px 64px 0;
|
||||||
|
height: calc(100vh - 153px);
|
||||||
|
height: calc(100dvh - 153px);
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
padding: 12px 0;
|
||||||
|
flex: 4;
|
||||||
|
height: calc(100vh - 81px);
|
||||||
|
height: calc(100dvh - 81px);
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
.sidebar.hidden {
|
||||||
|
border-color: transparent;
|
||||||
|
border-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.overlay {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
height: calc(100% - 64px);
|
||||||
|
padding: 0;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 870px) {
|
||||||
|
.sidebar.overlay {
|
||||||
|
max-height: 70vh;
|
||||||
|
max-height: 70dvh;
|
||||||
|
height: auto;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0px -8px 16px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 870px) {
|
||||||
|
.sidebar.overlay.hidden {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.overlay.hidden {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@@ -559,6 +702,11 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
font-weight: var(--ha-font-weight-normal);
|
font-weight: var(--ha-font-weight-normal);
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ha-alert {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -568,4 +716,9 @@ declare global {
|
|||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"manual-automation-editor": HaManualAutomationEditor;
|
"manual-automation-editor": HaManualAutomationEditor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"open-sidebar": OpenSidebarConfig;
|
||||||
|
"close-sidebar": undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { consume } from "@lit/context";
|
import { consume } from "@lit/context";
|
||||||
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
mdiArrowDown,
|
mdiArrowDown,
|
||||||
mdiArrowUp,
|
mdiArrowUp,
|
||||||
@@ -12,16 +10,19 @@ import {
|
|||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { ensureArray } from "../../../../common/array/ensure-array";
|
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { preventDefault } from "../../../../common/dom/prevent_default";
|
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
||||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||||
import "../../../../components/ha-button-menu";
|
import "../../../../components/ha-automation-row";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-expansion-panel";
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
import "../../../../components/ha-list-item";
|
import "../../../../components/ha-md-button-menu";
|
||||||
|
import "../../../../components/ha-md-menu-item";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
import type { Condition } from "../../../../data/automation";
|
import type { Condition } from "../../../../data/automation";
|
||||||
import { describeCondition } from "../../../../data/automation_i18n";
|
import { describeCondition } from "../../../../data/automation_i18n";
|
||||||
import { fullEntitiesContext } from "../../../../data/context";
|
import { fullEntitiesContext } from "../../../../data/context";
|
||||||
@@ -31,10 +32,10 @@ import {
|
|||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
showPromptDialog,
|
showPromptDialog,
|
||||||
} from "../../../../dialogs/generic/show-dialog-box";
|
} from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import "../action/ha-automation-action";
|
import "../action/ha-automation-action";
|
||||||
import "../condition/ha-automation-condition";
|
import "../condition/ha-automation-condition";
|
||||||
|
import { editorStyles, rowStyles } from "../styles";
|
||||||
|
|
||||||
@customElement("ha-automation-option-row")
|
@customElement("ha-automation-option-row")
|
||||||
export default class HaAutomationOptionRow extends LitElement {
|
export default class HaAutomationOptionRow extends LitElement {
|
||||||
@@ -52,8 +53,15 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public last = false;
|
@property({ type: Boolean }) public last = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" })
|
||||||
|
public optionsInSidebar = false;
|
||||||
|
|
||||||
@state() private _expanded = false;
|
@state() private _expanded = false;
|
||||||
|
|
||||||
|
@state() private _selected = false;
|
||||||
|
|
||||||
|
@state() private _collapsed = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entityReg!: EntityRegistryEntry[];
|
_entityReg!: EntityRegistryEntry[];
|
||||||
@@ -87,144 +95,175 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderRow() {
|
||||||
|
return html`
|
||||||
|
<h3 slot="header">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.choose.option",
|
||||||
|
{ number: this.index + 1 }
|
||||||
|
)}:
|
||||||
|
${this.option.alias || (this._expanded ? "" : this._getDescription())}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<slot name="icons" slot="icons"></slot>
|
||||||
|
|
||||||
|
<ha-md-button-menu
|
||||||
|
slot="icons"
|
||||||
|
@click=${preventDefaultStopPropagation}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
@keydown=${stopPropagation}
|
||||||
|
positioning="fixed"
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
.path=${mdiDotsVertical}
|
||||||
|
></ha-icon-button>
|
||||||
|
|
||||||
|
${!this.optionsInSidebar
|
||||||
|
? html`
|
||||||
|
<ha-md-menu-item
|
||||||
|
@click=${this._renameOption}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.rename"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
@click=${this._duplicateOption}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.duplicate"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiContentDuplicate}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
@click=${this._moveUp}
|
||||||
|
.disabled=${this.disabled || this.first}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
||||||
|
<ha-svg-icon slot="graphic" .path=${mdiArrowUp}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
@click=${this._moveDown}
|
||||||
|
.disabled=${this.disabled || this.last}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.panel.config.automation.editor.move_down")}
|
||||||
|
<ha-svg-icon slot="graphic" .path=${mdiArrowDown}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
@click=${this._removeOption}
|
||||||
|
class="warning"
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.choose.remove_option"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class="warning"
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiDelete}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
</ha-md-button-menu>
|
||||||
|
|
||||||
|
${!this.optionsInSidebar ? this._renderContent() : nothing}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderContent() {
|
||||||
|
return html`<div
|
||||||
|
class=${classMap({
|
||||||
|
"card-content": true,
|
||||||
|
indent: this.optionsInSidebar,
|
||||||
|
selected: this._selected,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<h4>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.choose.conditions"
|
||||||
|
)}:
|
||||||
|
</h4>
|
||||||
|
<ha-automation-condition
|
||||||
|
.conditions=${ensureArray<string | Condition>(this.option.conditions)}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@value-changed=${this._conditionChanged}
|
||||||
|
.optionsInSidebar=${this.optionsInSidebar}
|
||||||
|
></ha-automation-condition>
|
||||||
|
<h4>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.choose.sequence"
|
||||||
|
)}:
|
||||||
|
</h4>
|
||||||
|
<ha-automation-action
|
||||||
|
.actions=${ensureArray(this.option.sequence) || []}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@value-changed=${this._actionChanged}
|
||||||
|
.optionsInSidebar=${this.optionsInSidebar}
|
||||||
|
></ha-automation-action>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.option) return nothing;
|
if (!this.option) return nothing;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card outlined>
|
<ha-card outlined class=${this._selected ? "selected" : ""}>
|
||||||
<ha-expansion-panel
|
${this.optionsInSidebar
|
||||||
left-chevron
|
? html`<ha-automation-row
|
||||||
@expanded-changed=${this._expandedChanged}
|
left-chevron
|
||||||
id="option"
|
.collapsed=${this._collapsed}
|
||||||
>
|
.selected=${this._selected}
|
||||||
<h3 slot="header">
|
@click=${this._toggleSidebar}
|
||||||
${this.hass.localize(
|
@toggle-collapsed=${this._toggleCollapse}
|
||||||
"ui.panel.config.automation.editor.actions.type.choose.option",
|
>${this._renderRow()}</ha-automation-row
|
||||||
{ number: this.index + 1 }
|
>`
|
||||||
)}:
|
: html`
|
||||||
${this.option.alias ||
|
<ha-expansion-panel
|
||||||
(this._expanded ? "" : this._getDescription())}
|
left-chevron
|
||||||
</h3>
|
@expanded-changed=${this._expandedChanged}
|
||||||
|
id="option"
|
||||||
<slot name="icons" slot="icons"></slot>
|
>
|
||||||
|
${this._renderRow()}
|
||||||
<ha-button-menu
|
</ha-expansion-panel>
|
||||||
slot="icons"
|
`}
|
||||||
@action=${this._handleAction}
|
|
||||||
@click=${preventDefault}
|
|
||||||
@closed=${stopPropagation}
|
|
||||||
fixed
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
slot="trigger"
|
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
|
||||||
.path=${mdiDotsVertical}
|
|
||||||
></ha-icon-button>
|
|
||||||
<ha-list-item graphic="icon" .disabled=${this.disabled}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.rename"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
|
|
||||||
</ha-list-item>
|
|
||||||
|
|
||||||
<ha-list-item graphic="icon" .disabled=${this.disabled}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.duplicate"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="graphic"
|
|
||||||
.path=${mdiContentDuplicate}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-list-item>
|
|
||||||
|
|
||||||
<ha-list-item
|
|
||||||
graphic="icon"
|
|
||||||
.disabled=${this.disabled || this.first}
|
|
||||||
>
|
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
|
||||||
<ha-svg-icon slot="graphic" .path=${mdiArrowUp}></ha-svg-icon>
|
|
||||||
</ha-list-item>
|
|
||||||
|
|
||||||
<ha-list-item
|
|
||||||
graphic="icon"
|
|
||||||
.disabled=${this.disabled || this.last}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.move_down"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="graphic" .path=${mdiArrowDown}></ha-svg-icon>
|
|
||||||
</ha-list-item>
|
|
||||||
|
|
||||||
<ha-list-item
|
|
||||||
class="warning"
|
|
||||||
graphic="icon"
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type.choose.remove_option"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
class="warning"
|
|
||||||
slot="graphic"
|
|
||||||
.path=${mdiDelete}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-list-item>
|
|
||||||
</ha-button-menu>
|
|
||||||
|
|
||||||
<div class="card-content">
|
|
||||||
<h4>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type.choose.conditions"
|
|
||||||
)}:
|
|
||||||
</h4>
|
|
||||||
<ha-automation-condition
|
|
||||||
.conditions=${ensureArray<string | Condition>(
|
|
||||||
this.option.conditions
|
|
||||||
)}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
.hass=${this.hass}
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
@value-changed=${this._conditionChanged}
|
|
||||||
></ha-automation-condition>
|
|
||||||
<h4>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type.choose.sequence"
|
|
||||||
)}:
|
|
||||||
</h4>
|
|
||||||
<ha-automation-action
|
|
||||||
.actions=${ensureArray(this.option.sequence) || []}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
.hass=${this.hass}
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
@value-changed=${this._actionChanged}
|
|
||||||
></ha-automation-action>
|
|
||||||
</div>
|
|
||||||
</ha-expansion-panel>
|
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
|
||||||
|
${this.optionsInSidebar && !this._collapsed
|
||||||
|
? this._renderContent()
|
||||||
|
: nothing}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleAction(ev: CustomEvent<ActionDetail>) {
|
private _duplicateOption() {
|
||||||
switch (ev.detail.index) {
|
fireEvent(this, "duplicate");
|
||||||
case 0:
|
|
||||||
await this._renameOption();
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
fireEvent(this, "duplicate");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
fireEvent(this, "move-up");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
fireEvent(this, "move-down");
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
this._removeOption();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _removeOption() {
|
private _moveUp() {
|
||||||
|
fireEvent(this, "move-up");
|
||||||
|
}
|
||||||
|
|
||||||
|
private _moveDown() {
|
||||||
|
fireEvent(this, "move-down");
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removeOption = () => {
|
||||||
showConfirmationDialog(this, {
|
showConfirmationDialog(this, {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.type.choose.delete_confirm_title"
|
"ui.panel.config.automation.editor.actions.type.choose.delete_confirm_title"
|
||||||
@@ -235,14 +274,18 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
dismissText: this.hass.localize("ui.common.cancel"),
|
dismissText: this.hass.localize("ui.common.cancel"),
|
||||||
confirmText: this.hass.localize("ui.common.delete"),
|
confirmText: this.hass.localize("ui.common.delete"),
|
||||||
destructive: true,
|
destructive: true,
|
||||||
confirm: () =>
|
confirm: () => {
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: null,
|
value: null,
|
||||||
}),
|
});
|
||||||
|
if (this._selected) {
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
private async _renameOption(): Promise<void> {
|
private _renameOption = async () => {
|
||||||
const alias = await showPromptDialog(this, {
|
const alias = await showPromptDialog(this, {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.type.choose.change_alias"
|
"ui.panel.config.automation.editor.actions.type.choose.change_alias"
|
||||||
@@ -266,7 +309,7 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
private _conditionChanged(ev: CustomEvent) {
|
private _conditionChanged(ev: CustomEvent) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
@@ -286,46 +329,61 @@ export default class HaAutomationOptionRow extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _toggleSidebar(ev: Event) {
|
||||||
|
ev?.stopPropagation();
|
||||||
|
|
||||||
|
if (this._selected) {
|
||||||
|
this._selected = false;
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openSidebar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public openSidebar(): void {
|
||||||
|
if (this.narrow) {
|
||||||
|
this.scrollIntoView();
|
||||||
|
}
|
||||||
|
|
||||||
|
fireEvent(this, "open-sidebar", {
|
||||||
|
save: () => {
|
||||||
|
// nothing to save for an option in the sidebar
|
||||||
|
},
|
||||||
|
close: () => {
|
||||||
|
this._selected = false;
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
},
|
||||||
|
rename: () => {
|
||||||
|
this._renameOption();
|
||||||
|
},
|
||||||
|
toggleYamlMode: () => false, // no yaml mode for options
|
||||||
|
disable: () => {
|
||||||
|
// option cannot be disabled
|
||||||
|
},
|
||||||
|
delete: this._removeOption,
|
||||||
|
config: {},
|
||||||
|
type: "option",
|
||||||
|
uiSupported: true,
|
||||||
|
yamlMode: false,
|
||||||
|
});
|
||||||
|
this._selected = true;
|
||||||
|
}
|
||||||
|
|
||||||
public expand() {
|
public expand() {
|
||||||
this.updateComplete.then(() => {
|
this.updateComplete.then(() => {
|
||||||
this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = true;
|
this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _toggleCollapse() {
|
||||||
|
this._collapsed = !this._collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
rowStyles,
|
||||||
|
editorStyles,
|
||||||
css`
|
css`
|
||||||
ha-button-menu,
|
|
||||||
ha-icon-button {
|
|
||||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
.disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
ha-expansion-panel {
|
|
||||||
--expansion-panel-summary-padding: 0 0 0 8px;
|
|
||||||
--expansion-panel-content-padding: 0;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
.card-content {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-list-item[disabled] {
|
|
||||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
|
||||||
}
|
|
||||||
ha-list-item.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.warning ul {
|
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
li[role="separator"] {
|
li[role="separator"] {
|
||||||
border-bottom-color: var(--divider-color);
|
border-bottom-color: var(--divider-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ export default class HaAutomationOption extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public options!: Option[];
|
@property({ attribute: false }) public options!: Option[];
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" }) public optionsInSidebar =
|
||||||
|
false;
|
||||||
|
|
||||||
@state() private _showReorder = false;
|
@state() private _showReorder = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
@@ -87,6 +90,7 @@ export default class HaAutomationOption extends LitElement {
|
|||||||
@move-up=${this._moveUp}
|
@move-up=${this._moveUp}
|
||||||
@value-changed=${this._optionChanged}
|
@value-changed=${this._optionChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.optionsInSidebar=${this.optionsInSidebar}
|
||||||
>
|
>
|
||||||
${this._showReorder && !this.disabled
|
${this._showReorder && !this.disabled
|
||||||
? html`
|
? html`
|
||||||
@@ -101,6 +105,7 @@ export default class HaAutomationOption extends LitElement {
|
|||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<ha-button
|
<ha-button
|
||||||
appearance="filled"
|
appearance="filled"
|
||||||
|
size="small"
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@click=${this._addOption}
|
@click=${this._addOption}
|
||||||
>
|
>
|
||||||
@@ -125,7 +130,9 @@ export default class HaAutomationOption extends LitElement {
|
|||||||
"ha-automation-option-row:last-of-type"
|
"ha-automation-option-row:last-of-type"
|
||||||
)!;
|
)!;
|
||||||
row.updateComplete.then(() => {
|
row.updateComplete.then(() => {
|
||||||
row.expand();
|
if (!this.optionsInSidebar) {
|
||||||
|
row.expand();
|
||||||
|
}
|
||||||
row.scrollIntoView();
|
row.scrollIntoView();
|
||||||
row.focus();
|
row.focus();
|
||||||
});
|
});
|
||||||
@@ -238,7 +245,7 @@ export default class HaAutomationOption extends LitElement {
|
|||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
.options {
|
.options {
|
||||||
padding: 16px;
|
padding: 16px 0 16px 16px;
|
||||||
margin: -16px;
|
margin: -16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -246,7 +253,7 @@ export default class HaAutomationOption extends LitElement {
|
|||||||
}
|
}
|
||||||
.sortable-ghost {
|
.sortable-ghost {
|
||||||
background: none;
|
background: none;
|
||||||
border-radius: var(--ha-card-border-radius, 12px);
|
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
||||||
}
|
}
|
||||||
.sortable-drag {
|
.sortable-drag {
|
||||||
background: none;
|
background: none;
|
||||||
|
|||||||
90
src/panels/config/automation/styles.ts
Normal file
90
src/panels/config/automation/styles.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import { css } from "lit";
|
||||||
|
|
||||||
|
export const rowStyles = css`
|
||||||
|
ha-icon-button {
|
||||||
|
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
ha-expansion-panel {
|
||||||
|
--expansion-panel-summary-padding: 0 0 0 8px;
|
||||||
|
--expansion-panel-content-padding: 0;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-card {
|
||||||
|
transition: outline 0.2s;
|
||||||
|
}
|
||||||
|
.disabled-bar {
|
||||||
|
background: var(--divider-color, #e0e0e0);
|
||||||
|
text-align: center;
|
||||||
|
border-top-right-radius: var(
|
||||||
|
--ha-card-border-radius,
|
||||||
|
var(--ha-border-radius-lg)
|
||||||
|
);
|
||||||
|
border-top-left-radius: var(
|
||||||
|
--ha-card-border-radius,
|
||||||
|
var(--ha-border-radius-lg)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.warning ul {
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
ha-md-menu-item > ha-svg-icon {
|
||||||
|
--mdc-icon-size: 24px;
|
||||||
|
}
|
||||||
|
ha-tooltip {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
:host([highlight]) ha-card {
|
||||||
|
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
|
||||||
|
--shadow-focus: 0 0 0 1px var(--state-inactive-color);
|
||||||
|
border-color: var(--state-inactive-color);
|
||||||
|
box-shadow: var(--shadow-default), var(--shadow-focus);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const editorStyles = css`
|
||||||
|
.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.card-content.yaml {
|
||||||
|
padding: 0 1px;
|
||||||
|
border-top: 1px solid var(--divider-color);
|
||||||
|
border-bottom: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
.card-content.indent {
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-right: -4px;
|
||||||
|
padding: 12px 24px 16px 16px;
|
||||||
|
border-left: 2px solid var(--ha-color-border-neutral-quiet);
|
||||||
|
}
|
||||||
|
.card-content.indent.selected,
|
||||||
|
:host([selected]) .card-content.indent {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
background-color: var(--ha-color-fill-primary-quiet-resting);
|
||||||
|
border-top-right-radius: var(--ha-border-radius-xl);
|
||||||
|
border-bottom-right-radius: var(--ha-border-radius-xl);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const saveFabStyles = css`
|
||||||
|
:host {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
ha-fab {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
bottom: calc(-80px - var(--safe-area-inset-bottom));
|
||||||
|
transition: bottom 0.3s;
|
||||||
|
}
|
||||||
|
ha-fab.dirty {
|
||||||
|
bottom: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
import type { CSSResultGroup } from "lit";
|
||||||
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-textfield";
|
||||||
|
import "../../../../components/ha-yaml-editor";
|
||||||
|
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
|
||||||
|
import type { Trigger } from "../../../../data/automation";
|
||||||
|
import { migrateAutomationTrigger } from "../../../../data/automation";
|
||||||
|
import { isTriggerList } from "../../../../data/trigger";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import "../ha-automation-editor-warning";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-editor")
|
||||||
|
export default class HaAutomationTriggerEditor extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public trigger!: Trigger;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "yaml" }) public yamlMode = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "supported" }) public uiSupported =
|
||||||
|
false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "show-id" }) public showId = false;
|
||||||
|
|
||||||
|
@query("ha-yaml-editor") public yamlEditor?: HaYamlEditor;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const type = isTriggerList(this.trigger) ? "list" : this.trigger.trigger;
|
||||||
|
|
||||||
|
const yamlMode = this.yamlMode || !this.uiSupported;
|
||||||
|
const showId = "id" in this.trigger || this.showId;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class=${classMap({
|
||||||
|
"card-content": true,
|
||||||
|
disabled:
|
||||||
|
this.disabled ||
|
||||||
|
("enabled" in this.trigger &&
|
||||||
|
this.trigger.enabled === false &&
|
||||||
|
!this.yamlMode),
|
||||||
|
yaml: yamlMode,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
${yamlMode
|
||||||
|
? html`
|
||||||
|
${!this.uiSupported
|
||||||
|
? html`
|
||||||
|
<ha-automation-editor-warning
|
||||||
|
.alertTitle=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.unsupported_platform",
|
||||||
|
{ platform: type }
|
||||||
|
)}
|
||||||
|
.localize=${this.hass.localize}
|
||||||
|
></ha-automation-editor-warning>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
<ha-yaml-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.defaultValue=${this.trigger}
|
||||||
|
.readOnly=${this.disabled}
|
||||||
|
@value-changed=${this._onYamlChange}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
${showId && !isTriggerList(this.trigger)
|
||||||
|
? html`
|
||||||
|
<ha-textfield
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.id"
|
||||||
|
)}
|
||||||
|
.value=${this.trigger.id || ""}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
@change=${this._idChanged}
|
||||||
|
>
|
||||||
|
</ha-textfield>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<div @value-changed=${this._onUiChanged}>
|
||||||
|
${dynamicElement(`ha-automation-trigger-${type}`, {
|
||||||
|
hass: this.hass,
|
||||||
|
trigger: this.trigger,
|
||||||
|
disabled: this.disabled,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _idChanged(ev: CustomEvent) {
|
||||||
|
if (isTriggerList(this.trigger)) return;
|
||||||
|
const newId = (ev.target as any).value;
|
||||||
|
|
||||||
|
if (newId === (this.trigger.id ?? "")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const value = { ...this.trigger };
|
||||||
|
if (!newId) {
|
||||||
|
delete value.id;
|
||||||
|
} else {
|
||||||
|
value.id = newId;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onYamlChange(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!ev.detail.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: migrateAutomationTrigger(ev.detail.value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onUiChanged(ev: CustomEvent) {
|
||||||
|
if (isTriggerList(this.trigger)) return;
|
||||||
|
ev.stopPropagation();
|
||||||
|
const value = {
|
||||||
|
...(this.trigger.alias ? { alias: this.trigger.alias } : {}),
|
||||||
|
...ev.detail.value,
|
||||||
|
};
|
||||||
|
fireEvent(this, "value-changed", { value });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.card-content.yaml {
|
||||||
|
padding: 0 1px;
|
||||||
|
border-top: 1px solid var(--divider-color);
|
||||||
|
border-bottom: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
ha-textfield {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-editor": HaAutomationTriggerEditor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,28 +18,24 @@ import type { CSSResultGroup, PropertyValues } from "lit";
|
|||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import { storage } from "../../../../common/decorators/storage";
|
import { storage } from "../../../../common/decorators/storage";
|
||||||
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { preventDefault } from "../../../../common/dom/prevent_default";
|
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
||||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
import { debounce } from "../../../../common/util/debounce";
|
import { debounce } from "../../../../common/util/debounce";
|
||||||
import "../../../../components/ha-alert";
|
import "../../../../components/ha-alert";
|
||||||
import "../../../../components/ha-md-button-menu";
|
import "../../../../components/ha-automation-row";
|
||||||
import "../../../../components/ha-md-menu-item";
|
|
||||||
import "../../../../components/ha-md-divider";
|
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-expansion-panel";
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
import "../../../../components/ha-textfield";
|
import "../../../../components/ha-md-button-menu";
|
||||||
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
|
import "../../../../components/ha-md-divider";
|
||||||
|
import "../../../../components/ha-md-menu-item";
|
||||||
import type { AutomationClipboard, Trigger } from "../../../../data/automation";
|
import type { AutomationClipboard, Trigger } from "../../../../data/automation";
|
||||||
import {
|
import { subscribeTrigger } from "../../../../data/automation";
|
||||||
migrateAutomationTrigger,
|
|
||||||
subscribeTrigger,
|
|
||||||
} from "../../../../data/automation";
|
|
||||||
import { describeTrigger } from "../../../../data/automation_i18n";
|
import { describeTrigger } from "../../../../data/automation_i18n";
|
||||||
import { validateConfig } from "../../../../data/config";
|
import { validateConfig } from "../../../../data/config";
|
||||||
import { fullEntitiesContext } from "../../../../data/context";
|
import { fullEntitiesContext } from "../../../../data/context";
|
||||||
@@ -50,8 +46,11 @@ import {
|
|||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
showPromptDialog,
|
showPromptDialog,
|
||||||
} from "../../../../dialogs/generic/show-dialog-box";
|
} from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import "../ha-automation-editor-warning";
|
||||||
|
import { rowStyles } from "../styles";
|
||||||
|
import "./ha-automation-trigger-editor";
|
||||||
|
import type HaAutomationTriggerEditor from "./ha-automation-trigger-editor";
|
||||||
import "./types/ha-automation-trigger-calendar";
|
import "./types/ha-automation-trigger-calendar";
|
||||||
import "./types/ha-automation-trigger-conversation";
|
import "./types/ha-automation-trigger-conversation";
|
||||||
import "./types/ha-automation-trigger-device";
|
import "./types/ha-automation-trigger-device";
|
||||||
@@ -109,17 +108,25 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public last?: boolean;
|
@property({ type: Boolean }) public last?: boolean;
|
||||||
|
|
||||||
@state() private _warnings?: string[];
|
@property({ type: Boolean, attribute: "sidebar" })
|
||||||
|
public optionsInSidebar = false;
|
||||||
|
|
||||||
@state() private _yamlMode = false;
|
@state() private _yamlMode = false;
|
||||||
|
|
||||||
@state() private _requestShowId = false;
|
|
||||||
|
|
||||||
@state() private _triggered?: Record<string, unknown>;
|
@state() private _triggered?: Record<string, unknown>;
|
||||||
|
|
||||||
@state() private _triggerColor = false;
|
@state() private _triggerColor = false;
|
||||||
|
|
||||||
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
|
@state() private _selected = false;
|
||||||
|
|
||||||
|
@state() private _requestShowId = false;
|
||||||
|
|
||||||
|
@state() private _warnings?: string[];
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@query("ha-automation-trigger-editor")
|
||||||
|
public triggerEditor?: HaAutomationTriggerEditor;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "automationClipboard",
|
key: "automationClipboard",
|
||||||
@@ -135,19 +142,186 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
|
|
||||||
private _triggerUnsub?: Promise<UnsubscribeFunc>;
|
private _triggerUnsub?: Promise<UnsubscribeFunc>;
|
||||||
|
|
||||||
|
private _renderRow() {
|
||||||
|
const type = this._getType(this.trigger);
|
||||||
|
|
||||||
|
const supported = this._uiSupported(type);
|
||||||
|
|
||||||
|
const yamlMode = this._yamlMode || !supported;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="leading-icon"
|
||||||
|
class="trigger-icon"
|
||||||
|
.path=${TRIGGER_ICONS[type]}
|
||||||
|
></ha-svg-icon>
|
||||||
|
<h3 slot="header">
|
||||||
|
${describeTrigger(this.trigger, this.hass, this._entityReg)}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<slot name="icons" slot="icons"></slot>
|
||||||
|
|
||||||
|
<ha-md-button-menu
|
||||||
|
slot="icons"
|
||||||
|
@click=${preventDefaultStopPropagation}
|
||||||
|
@keydown=${stopPropagation}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
positioning="fixed"
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
.path=${mdiDotsVertical}
|
||||||
|
></ha-icon-button>
|
||||||
|
|
||||||
|
${!this.optionsInSidebar
|
||||||
|
? html` <ha-md-menu-item
|
||||||
|
.clickAction=${this._renameTrigger}
|
||||||
|
.disabled=${this.disabled || type === "list"}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.rename"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._showTriggerId}
|
||||||
|
.disabled=${this.disabled || type === "list"}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.edit_id"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiIdentifier}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>`
|
||||||
|
: nothing}
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._duplicateTrigger}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.duplicate"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentDuplicate}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._copyTrigger}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.copy"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._cutTrigger}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.cut"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._moveUp}
|
||||||
|
.disabled=${this.disabled || this.first}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||||
|
></ha-md-menu-item>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._moveDown}
|
||||||
|
.disabled=${this.disabled || this.last}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.panel.config.automation.editor.move_down")}
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||||
|
></ha-md-menu-item>
|
||||||
|
|
||||||
|
${!this.optionsInSidebar
|
||||||
|
? html`
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._toggleYamlMode}
|
||||||
|
.disabled=${!supported || !!this._warnings}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiPlaylistEdit}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
|
||||||
|
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||||
|
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._onDisable}
|
||||||
|
.disabled=${this.disabled || type === "list"}
|
||||||
|
>
|
||||||
|
${"enabled" in this.trigger && this.trigger.enabled === false
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.enable"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.disable"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${"enabled" in this.trigger && this.trigger.enabled === false
|
||||||
|
? mdiPlayCircleOutline
|
||||||
|
: mdiStopCircleOutline}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
<ha-md-menu-item
|
||||||
|
.clickAction=${this._onDelete}
|
||||||
|
class="warning"
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class="warning"
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiDelete}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-md-menu-item>
|
||||||
|
</ha-md-button-menu>
|
||||||
|
${!this.optionsInSidebar
|
||||||
|
? html`${this._warnings
|
||||||
|
? html`<ha-automation-editor-warning
|
||||||
|
.localize=${this.hass.localize}
|
||||||
|
.warnings=${this._warnings}
|
||||||
|
>
|
||||||
|
</ha-automation-editor-warning>`
|
||||||
|
: nothing}
|
||||||
|
<ha-automation-trigger-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.trigger=${this.trigger}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.yamlMode=${this._yamlMode}
|
||||||
|
.showId=${this._requestShowId}
|
||||||
|
.uiSupported=${supported}
|
||||||
|
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
||||||
|
></ha-automation-trigger-editor>`
|
||||||
|
: nothing}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.trigger) return nothing;
|
if (!this.trigger) return nothing;
|
||||||
|
|
||||||
const type = isTriggerList(this.trigger) ? "list" : this.trigger.trigger;
|
|
||||||
|
|
||||||
const supported =
|
|
||||||
customElements.get(`ha-automation-trigger-${type}`) !== undefined;
|
|
||||||
|
|
||||||
const yamlMode = this._yamlMode || !supported;
|
|
||||||
const showId = "id" in this.trigger || this._requestShowId;
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card outlined>
|
<ha-card outlined class=${this._selected ? "selected" : ""}>
|
||||||
${"enabled" in this.trigger && this.trigger.enabled === false
|
${"enabled" in this.trigger && this.trigger.enabled === false
|
||||||
? html`
|
? html`
|
||||||
<div class="disabled-bar">
|
<div class="disabled-bar">
|
||||||
@@ -157,223 +331,21 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
${this.optionsInSidebar
|
||||||
<ha-expansion-panel left-chevron>
|
? html`<ha-automation-row
|
||||||
<ha-svg-icon
|
.disabled=${"enabled" in this.trigger &&
|
||||||
slot="leading-icon"
|
this.trigger.enabled === false}
|
||||||
class="trigger-icon"
|
@click=${this._toggleSidebar}
|
||||||
.path=${TRIGGER_ICONS[type]}
|
.selected=${this._selected}
|
||||||
></ha-svg-icon>
|
>${this._selected
|
||||||
<h3 slot="header">
|
? "selected"
|
||||||
${describeTrigger(this.trigger, this.hass, this._entityReg)}
|
: nothing}${this._renderRow()}</ha-automation-row
|
||||||
</h3>
|
>`
|
||||||
|
: html`
|
||||||
<slot name="icons" slot="icons"></slot>
|
<ha-expansion-panel left-chevron>
|
||||||
|
${this._renderRow()}
|
||||||
<ha-md-button-menu
|
</ha-expansion-panel>
|
||||||
slot="icons"
|
`}
|
||||||
@click=${preventDefault}
|
|
||||||
@keydown=${stopPropagation}
|
|
||||||
@closed=${stopPropagation}
|
|
||||||
positioning="fixed"
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
slot="trigger"
|
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
|
||||||
.path=${mdiDotsVertical}
|
|
||||||
></ha-icon-button>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._renameTrigger}
|
|
||||||
.disabled=${this.disabled || type === "list"}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.rename"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._showTriggerId}
|
|
||||||
.disabled=${this.disabled || type === "list"}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.edit_id"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiIdentifier}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._duplicateTrigger}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.duplicate"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="start"
|
|
||||||
.path=${mdiContentDuplicate}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._copyTrigger}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.copy"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._cutTrigger}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.cut"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._moveUp}
|
|
||||||
.disabled=${this.disabled || this.first}
|
|
||||||
>
|
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
|
||||||
></ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._moveDown}
|
|
||||||
.disabled=${this.disabled || this.last}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.move_down"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
|
||||||
></ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._toggleYamlMode}
|
|
||||||
.disabled=${!supported}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
`ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
|
|
||||||
)}
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
|
|
||||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
|
||||||
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._onDisable}
|
|
||||||
.disabled=${this.disabled || type === "list"}
|
|
||||||
>
|
|
||||||
${"enabled" in this.trigger && this.trigger.enabled === false
|
|
||||||
? this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.enable"
|
|
||||||
)
|
|
||||||
: this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.disable"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="start"
|
|
||||||
.path=${"enabled" in this.trigger &&
|
|
||||||
this.trigger.enabled === false
|
|
||||||
? mdiPlayCircleOutline
|
|
||||||
: mdiStopCircleOutline}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
<ha-md-menu-item
|
|
||||||
.clickAction=${this._onDelete}
|
|
||||||
class="warning"
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
class="warning"
|
|
||||||
slot="start"
|
|
||||||
.path=${mdiDelete}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-md-menu-item>
|
|
||||||
</ha-md-button-menu>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class=${classMap({
|
|
||||||
"card-content": true,
|
|
||||||
disabled:
|
|
||||||
"enabled" in this.trigger && this.trigger.enabled === false,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
${this._warnings
|
|
||||||
? html`<ha-alert
|
|
||||||
alert-type="warning"
|
|
||||||
.title=${this.hass.localize(
|
|
||||||
"ui.errors.config.editor_not_supported"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
${this._warnings.length && this._warnings[0] !== undefined
|
|
||||||
? html` <ul>
|
|
||||||
${this._warnings.map(
|
|
||||||
(warning) => html`<li>${warning}</li>`
|
|
||||||
)}
|
|
||||||
</ul>`
|
|
||||||
: ""}
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.errors.config.edit_in_yaml_supported"
|
|
||||||
)}
|
|
||||||
</ha-alert>`
|
|
||||||
: ""}
|
|
||||||
${yamlMode
|
|
||||||
? html`
|
|
||||||
${!supported
|
|
||||||
? html`
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.unsupported_platform",
|
|
||||||
{ platform: type }
|
|
||||||
)}
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<ha-yaml-editor
|
|
||||||
.hass=${this.hass}
|
|
||||||
.defaultValue=${this.trigger}
|
|
||||||
.readOnly=${this.disabled}
|
|
||||||
@value-changed=${this._onYamlChange}
|
|
||||||
></ha-yaml-editor>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
${showId && !isTriggerList(this.trigger)
|
|
||||||
? html`
|
|
||||||
<ha-textfield
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.id"
|
|
||||||
)}
|
|
||||||
.value=${this.trigger.id || ""}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
@change=${this._idChanged}
|
|
||||||
>
|
|
||||||
</ha-textfield>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<div
|
|
||||||
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
|
||||||
@value-changed=${this._onUiChanged}
|
|
||||||
>
|
|
||||||
${dynamicElement(`ha-automation-trigger-${type}`, {
|
|
||||||
hass: this.hass,
|
|
||||||
trigger: this.trigger,
|
|
||||||
disabled: this.disabled,
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
`}
|
|
||||||
</div>
|
|
||||||
</ha-expansion-panel>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="triggered ${classMap({
|
class="triggered ${classMap({
|
||||||
active: this._triggered !== undefined,
|
active: this._triggered !== undefined,
|
||||||
@@ -389,6 +361,13 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected willUpdate(changedProperties) {
|
||||||
|
// on yaml toggle --> clear warnings
|
||||||
|
if (changedProperties.has("yamlMode")) {
|
||||||
|
this._warnings = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override updated(changedProps: PropertyValues<this>): void {
|
protected override updated(changedProps: PropertyValues<this>): void {
|
||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
if (changedProps.has("trigger")) {
|
if (changedProps.has("trigger")) {
|
||||||
@@ -474,6 +453,46 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _toggleSidebar(ev: Event) {
|
||||||
|
ev?.stopPropagation();
|
||||||
|
|
||||||
|
if (this._selected) {
|
||||||
|
this._selected = false;
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openSidebar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public openSidebar(trigger?: Trigger): void {
|
||||||
|
if (this.narrow) {
|
||||||
|
this.scrollIntoView();
|
||||||
|
}
|
||||||
|
fireEvent(this, "open-sidebar", {
|
||||||
|
save: (value) => {
|
||||||
|
fireEvent(this, "value-changed", { value });
|
||||||
|
},
|
||||||
|
close: () => {
|
||||||
|
this._selected = false;
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
},
|
||||||
|
rename: () => {
|
||||||
|
this._renameTrigger();
|
||||||
|
},
|
||||||
|
toggleYamlMode: () => {
|
||||||
|
this._toggleYamlMode();
|
||||||
|
return this._yamlMode;
|
||||||
|
},
|
||||||
|
disable: this._onDisable,
|
||||||
|
delete: this._onDelete,
|
||||||
|
config: trigger || this.trigger,
|
||||||
|
type: "trigger",
|
||||||
|
uiSupported: this._uiSupported(this._getType(trigger || this.trigger)),
|
||||||
|
yamlMode: this._yamlMode,
|
||||||
|
});
|
||||||
|
this._selected = true;
|
||||||
|
}
|
||||||
|
|
||||||
private _setClipboard() {
|
private _setClipboard() {
|
||||||
this._clipboard = {
|
this._clipboard = {
|
||||||
...this._clipboard,
|
...this._clipboard,
|
||||||
@@ -494,6 +513,10 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
destructive: true,
|
destructive: true,
|
||||||
confirm: () => {
|
confirm: () => {
|
||||||
fireEvent(this, "value-changed", { value: null });
|
fireEvent(this, "value-changed", { value: null });
|
||||||
|
|
||||||
|
if (this._selected) {
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -503,58 +526,18 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
const enabled = !(this.trigger.enabled ?? true);
|
const enabled = !(this.trigger.enabled ?? true);
|
||||||
const value = { ...this.trigger, enabled };
|
const value = { ...this.trigger, enabled };
|
||||||
fireEvent(this, "value-changed", { value });
|
fireEvent(this, "value-changed", { value });
|
||||||
if (this._yamlMode) {
|
this.openSidebar(value); // refresh sidebar
|
||||||
this._yamlEditor?.setValue(value);
|
|
||||||
|
if (this._yamlMode && !this.optionsInSidebar) {
|
||||||
|
this.triggerEditor?.yamlEditor?.setValue(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _idChanged(ev: CustomEvent) {
|
|
||||||
if (isTriggerList(this.trigger)) return;
|
|
||||||
const newId = (ev.target as any).value;
|
|
||||||
|
|
||||||
if (newId === (this.trigger.id ?? "")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._requestShowId = true;
|
|
||||||
const value = { ...this.trigger };
|
|
||||||
if (!newId) {
|
|
||||||
delete value.id;
|
|
||||||
} else {
|
|
||||||
value.id = newId;
|
|
||||||
}
|
|
||||||
fireEvent(this, "value-changed", {
|
|
||||||
value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onYamlChange(ev: CustomEvent) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
if (!ev.detail.isValid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._warnings = undefined;
|
|
||||||
fireEvent(this, "value-changed", {
|
|
||||||
value: migrateAutomationTrigger(ev.detail.value),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onUiChanged(ev: CustomEvent) {
|
|
||||||
if (isTriggerList(this.trigger)) return;
|
|
||||||
ev.stopPropagation();
|
|
||||||
const value = {
|
|
||||||
...(this.trigger.alias ? { alias: this.trigger.alias } : {}),
|
|
||||||
...ev.detail.value,
|
|
||||||
};
|
|
||||||
fireEvent(this, "value-changed", { value });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _switchUiMode() {
|
private _switchUiMode() {
|
||||||
this._warnings = undefined;
|
|
||||||
this._yamlMode = false;
|
this._yamlMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _switchYamlMode() {
|
private _switchYamlMode() {
|
||||||
this._warnings = undefined;
|
|
||||||
this._yamlMode = true;
|
this._yamlMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,15 +584,21 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
if (this._yamlMode) {
|
|
||||||
this._yamlEditor?.setValue(value);
|
if (this._selected && this.optionsInSidebar) {
|
||||||
|
this.openSidebar(value); // refresh sidebar
|
||||||
|
} else if (this._yamlMode) {
|
||||||
|
this.triggerEditor?.yamlEditor?.setValue(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _showTriggerId = () => {
|
private _showTriggerId = () => {
|
||||||
this._requestShowId = true;
|
this._requestShowId = true;
|
||||||
this.expand();
|
|
||||||
|
if (!this.optionsInSidebar) {
|
||||||
|
this.expand();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _duplicateTrigger = () => {
|
private _duplicateTrigger = () => {
|
||||||
@@ -623,6 +612,9 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
private _cutTrigger = () => {
|
private _cutTrigger = () => {
|
||||||
this._setClipboard();
|
this._setClipboard();
|
||||||
fireEvent(this, "value-changed", { value: null });
|
fireEvent(this, "value-changed", { value: null });
|
||||||
|
if (this._selected) {
|
||||||
|
fireEvent(this, "close-sidebar");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _moveUp = () => {
|
private _moveUp = () => {
|
||||||
@@ -639,7 +631,10 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
} else {
|
} else {
|
||||||
this._switchYamlMode();
|
this._switchYamlMode();
|
||||||
}
|
}
|
||||||
this.expand();
|
|
||||||
|
if (!this.optionsInSidebar) {
|
||||||
|
this.expand();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public expand() {
|
public expand() {
|
||||||
@@ -648,52 +643,19 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getType = memoizeOne((trigger: Trigger) =>
|
||||||
|
isTriggerList(trigger) ? "list" : trigger.trigger
|
||||||
|
);
|
||||||
|
|
||||||
|
private _uiSupported = memoizeOne(
|
||||||
|
(type: string) =>
|
||||||
|
customElements.get(`ha-automation-trigger-${type}`) !== undefined
|
||||||
|
);
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
rowStyles,
|
||||||
css`
|
css`
|
||||||
.disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
ha-expansion-panel {
|
|
||||||
--expansion-panel-summary-padding: 0 0 0 8px;
|
|
||||||
--expansion-panel-content-padding: 0;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
.trigger-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
@media (min-width: 870px) {
|
|
||||||
.trigger-icon {
|
|
||||||
display: inline-block;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.card-content {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
.disabled-bar {
|
|
||||||
background: var(--divider-color, #e0e0e0);
|
|
||||||
text-align: center;
|
|
||||||
border-top-right-radius: calc(
|
|
||||||
var(--ha-card-border-radius, 12px) - var(
|
|
||||||
--ha-card-border-width,
|
|
||||||
1px
|
|
||||||
)
|
|
||||||
);
|
|
||||||
border-top-left-radius: calc(
|
|
||||||
var(--ha-card-border-radius, 12px) - var(
|
|
||||||
--ha-card-border-width,
|
|
||||||
1px
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
.triggered {
|
.triggered {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -709,17 +671,13 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: max-height 0.3s;
|
transition: max-height 0.3s;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-top-right-radius: calc(
|
border-top-right-radius: var(
|
||||||
var(--ha-card-border-radius, 12px) - var(
|
--ha-card-border-radius,
|
||||||
--ha-card-border-width,
|
var(--ha-border-radius-lg)
|
||||||
1px
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
border-top-left-radius: calc(
|
border-top-left-radius: var(
|
||||||
var(--ha-card-border-radius, 12px) - var(
|
--ha-card-border-radius,
|
||||||
--ha-card-border-width,
|
var(--ha-border-radius-lg)
|
||||||
1px
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
.triggered.active {
|
.triggered.active {
|
||||||
@@ -732,19 +690,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
color: var(--text-accent-color, var(--text-primary-color));
|
color: var(--text-accent-color, var(--text-primary-color));
|
||||||
}
|
}
|
||||||
ha-textfield {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
ha-md-menu-item > ha-svg-icon {
|
|
||||||
--mdc-icon-size: 24px;
|
|
||||||
}
|
|
||||||
:host([highlight]) ha-card {
|
|
||||||
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
|
|
||||||
--shadow-focus: 0 0 0 1px var(--state-inactive-color);
|
|
||||||
border-color: var(--state-inactive-color);
|
|
||||||
box-shadow: var(--shadow-default), var(--shadow-focus);
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,13 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "sidebar" }) public optionsInSidebar =
|
||||||
|
false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public root = false;
|
||||||
|
|
||||||
@state() private _showReorder = false;
|
@state() private _showReorder = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
@@ -95,7 +102,9 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
@value-changed=${this._triggerChanged}
|
@value-changed=${this._triggerChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
|
.narrow=${this.narrow}
|
||||||
?highlight=${this.highlightedTriggers?.includes(trg)}
|
?highlight=${this.highlightedTriggers?.includes(trg)}
|
||||||
|
.optionsInSidebar=${this.optionsInSidebar}
|
||||||
>
|
>
|
||||||
${this._showReorder && !this.disabled
|
${this._showReorder && !this.disabled
|
||||||
? html`
|
? html`
|
||||||
@@ -111,6 +120,8 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
<ha-button
|
<ha-button
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@click=${this._addTriggerDialog}
|
@click=${this._addTriggerDialog}
|
||||||
|
.appearance=${this.root ? "accent" : "filled"}
|
||||||
|
.size=${this.root ? "medium" : "small"}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.add"
|
"ui.panel.config.automation.editor.triggers.add"
|
||||||
@@ -164,7 +175,11 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
"ha-automation-trigger-row:last-of-type"
|
"ha-automation-trigger-row:last-of-type"
|
||||||
)!;
|
)!;
|
||||||
row.updateComplete.then(() => {
|
row.updateComplete.then(() => {
|
||||||
row.expand();
|
if (this.optionsInSidebar) {
|
||||||
|
row.openSidebar();
|
||||||
|
} else {
|
||||||
|
row.expand();
|
||||||
|
}
|
||||||
row.scrollIntoView();
|
row.scrollIntoView();
|
||||||
row.focus();
|
row.focus();
|
||||||
});
|
});
|
||||||
@@ -279,15 +294,18 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
.triggers {
|
.triggers {
|
||||||
padding: 16px;
|
padding: 16px 0 16px 16px;
|
||||||
margin: -16px;
|
margin: -16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
:host([root]) .triggers {
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
.sortable-ghost {
|
.sortable-ghost {
|
||||||
background: none;
|
background: none;
|
||||||
border-radius: var(--ha-card-border-radius, 12px);
|
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
||||||
}
|
}
|
||||||
.sortable-drag {
|
.sortable-drag {
|
||||||
background: none;
|
background: none;
|
||||||
|
|||||||
@@ -3847,6 +3847,7 @@
|
|||||||
"type_select": "Trigger type",
|
"type_select": "Trigger type",
|
||||||
"unknown_trigger": "[%key:ui::panel::config::devices::automation::triggers::unknown_trigger%]",
|
"unknown_trigger": "[%key:ui::panel::config::devices::automation::triggers::unknown_trigger%]",
|
||||||
"triggering_event_detail": "Triggering event detail",
|
"triggering_event_detail": "Triggering event detail",
|
||||||
|
"trigger": "Trigger",
|
||||||
"groups": {
|
"groups": {
|
||||||
"entity": {
|
"entity": {
|
||||||
"label": "Entity",
|
"label": "Entity",
|
||||||
@@ -4107,6 +4108,7 @@
|
|||||||
"unsupported_condition": "No visual editor support for condition: {condition}",
|
"unsupported_condition": "No visual editor support for condition: {condition}",
|
||||||
"type_select": "Condition type",
|
"type_select": "Condition type",
|
||||||
"unknown_condition": "[%key:ui::panel::config::devices::automation::conditions::unknown_condition%]",
|
"unknown_condition": "[%key:ui::panel::config::devices::automation::conditions::unknown_condition%]",
|
||||||
|
"condition": "Condition",
|
||||||
"groups": {
|
"groups": {
|
||||||
"entity": {
|
"entity": {
|
||||||
"label": "Entity",
|
"label": "Entity",
|
||||||
@@ -4275,6 +4277,7 @@
|
|||||||
"unsupported_action": "No visual editor support for this action",
|
"unsupported_action": "No visual editor support for this action",
|
||||||
"type_select": "Action type",
|
"type_select": "Action type",
|
||||||
"continue_on_error": "Continue on error",
|
"continue_on_error": "Continue on error",
|
||||||
|
"action": "Action",
|
||||||
"groups": {
|
"groups": {
|
||||||
"helpers": {
|
"helpers": {
|
||||||
"label": "Helpers"
|
"label": "Helpers"
|
||||||
@@ -4441,6 +4444,8 @@
|
|||||||
"conditions": "Conditions",
|
"conditions": "Conditions",
|
||||||
"no_conditions": "[%key:ui::panel::config::devices::automation::conditions::no_conditions%]",
|
"no_conditions": "[%key:ui::panel::config::devices::automation::conditions::no_conditions%]",
|
||||||
"sequence": "Actions",
|
"sequence": "Actions",
|
||||||
|
"option_label": "Option",
|
||||||
|
"option_description": "Choose actions based on conditions",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "Choose what to do based on conditions (Similar to If-then, but more powerful).",
|
"picker": "Choose what to do based on conditions (Similar to If-then, but more powerful).",
|
||||||
"full": "Choose {number, plural,\n one {an option}\n other{between {number} options}\n}",
|
"full": "Choose {number, plural,\n one {an option}\n other{between {number} options}\n}",
|
||||||
|
|||||||
Reference in New Issue
Block a user