mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
Add support for enabling/disabling trigger/condition/action (#12493)
* Add support for enabling/disabling trigger/condition/action * Add more visual indication of disabled * review * margin * Dont make overflow transparent * Change color of bar
This commit is contained in:
parent
b8c55f2f65
commit
38b4090daa
@ -65,6 +65,7 @@ export interface BaseTrigger {
|
||||
platform: string;
|
||||
id?: string;
|
||||
variables?: Record<string, unknown>;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export interface StateTrigger extends BaseTrigger {
|
||||
@ -178,6 +179,7 @@ export type Trigger =
|
||||
interface BaseCondition {
|
||||
condition: string;
|
||||
alias?: string;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export interface LogicalCondition extends BaseCondition {
|
||||
|
@ -11,6 +11,7 @@ export interface DeviceAutomation {
|
||||
type?: string;
|
||||
subtype?: string;
|
||||
event?: string;
|
||||
enabled?: boolean;
|
||||
metadata?: { secondary: boolean };
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
literal,
|
||||
is,
|
||||
Describe,
|
||||
boolean,
|
||||
} from "superstruct";
|
||||
import { computeObjectId } from "../common/entity/compute_object_id";
|
||||
import { navigate } from "../common/navigate";
|
||||
@ -25,6 +26,7 @@ export const MODES_MAX = ["queued", "parallel"];
|
||||
|
||||
export const baseActionStruct = object({
|
||||
alias: optional(string()),
|
||||
enabled: optional(boolean()),
|
||||
});
|
||||
|
||||
const targetStruct = object({
|
||||
@ -88,15 +90,18 @@ export interface BlueprintScriptConfig extends ManualScriptConfig {
|
||||
use_blueprint: { path: string; input?: BlueprintInput };
|
||||
}
|
||||
|
||||
export interface EventAction {
|
||||
interface BaseAction {
|
||||
alias?: string;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export interface EventAction extends BaseAction {
|
||||
event: string;
|
||||
event_data?: Record<string, any>;
|
||||
event_data_template?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface ServiceAction {
|
||||
alias?: string;
|
||||
export interface ServiceAction extends BaseAction {
|
||||
service?: string;
|
||||
service_template?: string;
|
||||
entity_id?: string;
|
||||
@ -104,55 +109,48 @@ export interface ServiceAction {
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface DeviceAction {
|
||||
alias?: string;
|
||||
export interface DeviceAction extends BaseAction {
|
||||
type: string;
|
||||
device_id: string;
|
||||
domain: string;
|
||||
entity_id: string;
|
||||
}
|
||||
|
||||
export interface DelayActionParts {
|
||||
export interface DelayActionParts extends BaseAction {
|
||||
milliseconds?: number;
|
||||
seconds?: number;
|
||||
minutes?: number;
|
||||
hours?: number;
|
||||
days?: number;
|
||||
}
|
||||
export interface DelayAction {
|
||||
alias?: string;
|
||||
export interface DelayAction extends BaseAction {
|
||||
delay: number | Partial<DelayActionParts> | string;
|
||||
}
|
||||
|
||||
export interface ServiceSceneAction {
|
||||
alias?: string;
|
||||
export interface ServiceSceneAction extends BaseAction {
|
||||
service: "scene.turn_on";
|
||||
target?: { entity_id?: string };
|
||||
entity_id?: string;
|
||||
metadata: Record<string, unknown>;
|
||||
}
|
||||
export interface LegacySceneAction {
|
||||
alias?: string;
|
||||
export interface LegacySceneAction extends BaseAction {
|
||||
scene: string;
|
||||
}
|
||||
export type SceneAction = ServiceSceneAction | LegacySceneAction;
|
||||
|
||||
export interface WaitAction {
|
||||
alias?: string;
|
||||
export interface WaitAction extends BaseAction {
|
||||
wait_template: string;
|
||||
timeout?: number;
|
||||
continue_on_timeout?: boolean;
|
||||
}
|
||||
|
||||
export interface WaitForTriggerAction {
|
||||
alias?: string;
|
||||
export interface WaitForTriggerAction extends BaseAction {
|
||||
wait_for_trigger: Trigger | Trigger[];
|
||||
timeout?: number;
|
||||
continue_on_timeout?: boolean;
|
||||
}
|
||||
|
||||
export interface PlayMediaAction {
|
||||
alias?: string;
|
||||
export interface PlayMediaAction extends BaseAction {
|
||||
service: "media_player.play_media";
|
||||
target?: { entity_id?: string };
|
||||
entity_id?: string;
|
||||
@ -160,13 +158,11 @@ export interface PlayMediaAction {
|
||||
metadata: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface RepeatAction {
|
||||
alias?: string;
|
||||
export interface RepeatAction extends BaseAction {
|
||||
repeat: CountRepeat | WhileRepeat | UntilRepeat;
|
||||
}
|
||||
|
||||
interface BaseRepeat {
|
||||
alias?: string;
|
||||
interface BaseRepeat extends BaseAction {
|
||||
sequence: Action | Action[];
|
||||
}
|
||||
|
||||
@ -182,38 +178,32 @@ export interface UntilRepeat extends BaseRepeat {
|
||||
until: Condition[];
|
||||
}
|
||||
|
||||
export interface ChooseActionChoice {
|
||||
alias?: string;
|
||||
export interface ChooseActionChoice extends BaseAction {
|
||||
conditions: string | Condition[];
|
||||
sequence: Action | Action[];
|
||||
}
|
||||
|
||||
export interface ChooseAction {
|
||||
alias?: string;
|
||||
export interface ChooseAction extends BaseAction {
|
||||
choose: ChooseActionChoice | ChooseActionChoice[] | null;
|
||||
default?: Action | Action[];
|
||||
}
|
||||
|
||||
export interface IfAction {
|
||||
alias?: string;
|
||||
export interface IfAction extends BaseAction {
|
||||
if: string | Condition[];
|
||||
then: Action | Action[];
|
||||
else?: Action | Action[];
|
||||
}
|
||||
|
||||
export interface VariablesAction {
|
||||
alias?: string;
|
||||
export interface VariablesAction extends BaseAction {
|
||||
variables: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface StopAction {
|
||||
alias?: string;
|
||||
export interface StopAction extends BaseAction {
|
||||
stop: string;
|
||||
error?: boolean;
|
||||
}
|
||||
|
||||
interface UnknownAction {
|
||||
alias?: string;
|
||||
interface UnknownAction extends BaseAction {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,13 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
${this.action.enabled === false
|
||||
? html`<div class="disabled-bar">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disabled"
|
||||
)}
|
||||
</div>`
|
||||
: ""}
|
||||
<div class="card-menu">
|
||||
${this.index !== 0
|
||||
? html`
|
||||
@ -209,6 +215,15 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item>
|
||||
${this.action.enabled === false
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.enable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disable"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item class="warning">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
@ -216,6 +231,11 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
<div
|
||||
class="card-content ${this.action.enabled === false
|
||||
? "disabled"
|
||||
: ""}"
|
||||
>
|
||||
${this._warnings
|
||||
? html`<ha-alert
|
||||
alert-type="warning"
|
||||
@ -314,11 +334,23 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
fireEvent(this, "duplicate");
|
||||
break;
|
||||
case 3:
|
||||
this._onDisable();
|
||||
break;
|
||||
case 4:
|
||||
this._onDelete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private _onDisable() {
|
||||
const enabled = !(this.action.enabled ?? true);
|
||||
const value = { ...this.action, enabled };
|
||||
fireEvent(this, "value-changed", { value });
|
||||
if (this._yamlMode) {
|
||||
this._yamlEditor?.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
private async _runAction() {
|
||||
const validated = await validateConfig(this.hass, {
|
||||
action: this.action,
|
||||
@ -408,11 +440,27 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
.card-content {
|
||||
padding-top: 16px;
|
||||
margin-top: 0;
|
||||
}
|
||||
.disabled-bar {
|
||||
background: var(--divider-color, #e0e0e0);
|
||||
text-align: center;
|
||||
border-top-right-radius: var(--ha-card-border-radius);
|
||||
border-top-left-radius: var(--ha-card-border-radius);
|
||||
}
|
||||
.card-menu {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
float: right;
|
||||
z-index: 3;
|
||||
margin: 4px;
|
||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
:host-context([style*="direction: rtl;"]) .card-menu {
|
||||
right: initial;
|
||||
|
@ -2,7 +2,7 @@ import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||
import "../../../../components/ha-button-menu";
|
||||
@ -19,6 +19,7 @@ import { haStyle } from "../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import "./ha-automation-condition-editor";
|
||||
import { validateConfig } from "../../../../data/config";
|
||||
import { HaYamlEditor } from "../../../../components/ha-yaml-editor";
|
||||
|
||||
export interface ConditionElement extends LitElement {
|
||||
condition: Condition;
|
||||
@ -59,13 +60,21 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
|
||||
@state() private _warnings?: string[];
|
||||
|
||||
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
|
||||
|
||||
protected render() {
|
||||
if (!this.condition) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
${this.condition.enabled === false
|
||||
? html`<div class="disabled-bar">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disabled"
|
||||
)}
|
||||
</div>`
|
||||
: ""}
|
||||
<div class="card-menu">
|
||||
<ha-progress-button @click=${this._testCondition}>
|
||||
${this.hass.localize(
|
||||
@ -93,6 +102,15 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item>
|
||||
${this.condition.enabled === false
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.enable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disable"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item class="warning">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
@ -100,6 +118,11 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
<div
|
||||
class="card-content ${this.condition.enabled === false
|
||||
? "disabled"
|
||||
: ""}"
|
||||
>
|
||||
${this._warnings
|
||||
? html`<ha-alert
|
||||
alert-type="warning"
|
||||
@ -153,11 +176,23 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
fireEvent(this, "duplicate");
|
||||
break;
|
||||
case 2:
|
||||
this._onDisable();
|
||||
break;
|
||||
case 3:
|
||||
this._onDelete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private _onDisable() {
|
||||
const enabled = !(this.condition.enabled ?? true);
|
||||
const value = { ...this.condition, enabled };
|
||||
fireEvent(this, "value-changed", { value });
|
||||
if (this._yamlMode) {
|
||||
this._yamlEditor?.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
private _onDelete() {
|
||||
showConfirmationDialog(this, {
|
||||
text: this.hass.localize(
|
||||
@ -238,9 +273,24 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
.card-content {
|
||||
padding-top: 16px;
|
||||
margin-top: 0;
|
||||
}
|
||||
.disabled-bar {
|
||||
background: var(--divider-color, #e0e0e0);
|
||||
text-align: center;
|
||||
border-top-right-radius: var(--ha-card-border-radius);
|
||||
border-top-left-radius: var(--ha-card-border-radius);
|
||||
}
|
||||
.card-menu {
|
||||
float: right;
|
||||
z-index: 3;
|
||||
margin: 4px;
|
||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { object, optional, number, string } from "superstruct";
|
||||
import { object, optional, number, string, boolean } from "superstruct";
|
||||
|
||||
export const baseTriggerStruct = object({
|
||||
platform: string(),
|
||||
id: optional(string()),
|
||||
enabled: optional(boolean()),
|
||||
});
|
||||
|
||||
export const forDictStruct = object({
|
||||
|
@ -3,7 +3,7 @@ import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } 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 memoizeOne from "memoize-one";
|
||||
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
||||
@ -16,7 +16,7 @@ import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-button-menu";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-yaml-editor";
|
||||
import { HaYamlEditor } from "../../../../components/ha-yaml-editor";
|
||||
import "../../../../components/ha-select";
|
||||
import type { HaSelect } from "../../../../components/ha-select";
|
||||
import "../../../../components/ha-textfield";
|
||||
@ -104,6 +104,8 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
|
||||
@state() private _triggerColor = false;
|
||||
|
||||
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
|
||||
|
||||
private _triggerUnsub?: Promise<UnsubscribeFunc>;
|
||||
|
||||
private _processedTypes = memoizeOne(
|
||||
@ -126,7 +128,13 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
${this.trigger.enabled === false
|
||||
? html`<div class="disabled-bar">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disabled"
|
||||
)}
|
||||
</div>`
|
||||
: ""}
|
||||
<div class="card-menu">
|
||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
||||
<ha-icon-button
|
||||
@ -153,6 +161,15 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item>
|
||||
${this.trigger.enabled === false
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.enable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disable"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item class="warning">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
@ -160,6 +177,11 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
<div
|
||||
class="card-content ${this.trigger.enabled === false
|
||||
? "disabled"
|
||||
: ""}"
|
||||
>
|
||||
${this._warnings
|
||||
? html`<ha-alert
|
||||
alert-type="warning"
|
||||
@ -214,7 +236,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
|
||||
${showId
|
||||
? html`
|
||||
<ha-textfield
|
||||
@ -250,7 +271,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
protected override updated(changedProps: PropertyValues): void {
|
||||
protected override updated(changedProps: PropertyValues<this>): void {
|
||||
super.updated(changedProps);
|
||||
if (changedProps.has("trigger")) {
|
||||
this._subscribeTrigger();
|
||||
@ -347,6 +368,9 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
fireEvent(this, "duplicate");
|
||||
break;
|
||||
case 3:
|
||||
this._onDisable();
|
||||
break;
|
||||
case 4:
|
||||
this._onDelete();
|
||||
break;
|
||||
}
|
||||
@ -365,6 +389,15 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _onDisable() {
|
||||
const enabled = !(this.trigger.enabled ?? true);
|
||||
const value = { ...this.trigger, enabled };
|
||||
fireEvent(this, "value-changed", { value });
|
||||
if (this._yamlMode) {
|
||||
this._yamlEditor?.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
private _typeChanged(ev: CustomEvent) {
|
||||
const type = (ev.target as HaSelect).value;
|
||||
|
||||
@ -439,10 +472,27 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
.card-content {
|
||||
padding-top: 16px;
|
||||
margin-top: 0;
|
||||
}
|
||||
.disabled-bar {
|
||||
background: var(--divider-color, #e0e0e0);
|
||||
text-align: center;
|
||||
border-top-right-radius: var(--ha-card-border-radius);
|
||||
border-top-left-radius: var(--ha-card-border-radius);
|
||||
}
|
||||
.card-menu {
|
||||
float: right;
|
||||
z-index: 3;
|
||||
margin: 4px;
|
||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
:host-context([style*="direction: rtl;"]) .card-menu {
|
||||
float: left;
|
||||
|
@ -1675,7 +1675,7 @@
|
||||
"introduction": "The automation editor allows you to create and edit automations. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.",
|
||||
"learn_more": "Learn more about automations",
|
||||
"pick_automation": "Pick automation to edit",
|
||||
"no_automations": "We couldn’t find any automations",
|
||||
"no_automations": "We couldn't find any automations",
|
||||
"add_automation": "Create automation",
|
||||
"only_editable": "Only automations in automations.yaml are editable.",
|
||||
"dev_only_editable": "Only automations that have a unique ID assigned are debuggable.",
|
||||
@ -1961,6 +1961,9 @@
|
||||
"run_action_error": "Error running action",
|
||||
"run_action_success": "Action run successfully",
|
||||
"duplicate": "[%key:ui::panel::config::automation::editor::triggers::duplicate%]",
|
||||
"enable": "Enable",
|
||||
"disable": "Disable",
|
||||
"disabled": "Disabled",
|
||||
"delete": "[%key:ui::panel::mailbox::delete_button%]",
|
||||
"delete_confirm": "[%key:ui::panel::config::automation::editor::triggers::delete_confirm%]",
|
||||
"unsupported_action": "No visual editor support for action: {action}",
|
||||
|
Loading…
x
Reference in New Issue
Block a user