From 3bb5e95c505f18303a7a59462384181f18843cf7 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 27 Jun 2023 18:28:20 +0200 Subject: [PATCH] Show if automation is unavailable and why (#17048) --- src/common/entity/domain_icon.ts | 7 ++- .../config/automation/ha-automation-editor.ts | 50 +++++++++++++++++-- .../config/automation/ha-automation-picker.ts | 12 ++++- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/common/entity/domain_icon.ts b/src/common/entity/domain_icon.ts index fa92cc59b2..3ced61b3f8 100644 --- a/src/common/entity/domain_icon.ts +++ b/src/common/entity/domain_icon.ts @@ -32,6 +32,7 @@ import { mdiPowerPlugOff, mdiRestart, mdiRobot, + mdiRobotConfused, mdiRobotOff, mdiSpeaker, mdiSpeakerOff, @@ -92,7 +93,11 @@ export const domainIconWithoutDefault = ( return alarmPanelIcon(compareState); case "automation": - return compareState === "off" ? mdiRobotOff : mdiRobot; + return compareState === "unavailable" + ? mdiRobotConfused + : compareState === "off" + ? mdiRobotOff + : mdiRobot; case "binary_sensor": return binarySensorIcon(compareState, stateObj); diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index ecc97fb23d..9238852502 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -11,6 +11,7 @@ import { mdiPlay, mdiPlayCircleOutline, mdiRenameBox, + mdiRobotConfused, mdiStopCircleOutline, mdiTransitConnection, } from "@mdi/js"; @@ -22,6 +23,7 @@ import { TemplateResult, css, html, + nothing, } from "lit"; import { property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; @@ -62,6 +64,8 @@ import { showAutomationModeDialog } from "./automation-mode-dialog/show-dialog-a import { showAutomationRenameDialog } from "./automation-rename-dialog/show-dialog-automation-rename"; import "./blueprint-automation-editor"; import "./manual-automation-editor"; +import { UNAVAILABLE } from "../../../data/entity"; +import { validateConfig } from "../../../data/config"; declare global { interface HTMLElementTagNameMap { @@ -106,6 +110,8 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { @state() private _readOnly = false; + @state() private _validationErrors?: (string | TemplateResult)[]; + @query("ha-yaml-editor", true) private _yamlEditor?: HaYamlEditor; private _configSubscriptions: Record< @@ -291,9 +297,20 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { })}" @subscribe-automation-config=${this._subscribeAutomationConfig} > - ${this._errors - ? html` - ${this._errors} + ${this._errors || stateObj?.state === UNAVAILABLE + ? html` + ${this._errors || this._validationErrors} + ${stateObj?.state === UNAVAILABLE + ? html`` + : nothing} ` : ""} ${this._mode === "gui" @@ -427,6 +444,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { if (changedProps.has("entityId") && this.entityId) { getAutomationStateConfig(this.hass, this.entityId).then((c) => { this._config = this._normalizeConfig(c.config); + this._checkValidation(); }); this._entityId = this.entityId; this._dirty = false; @@ -455,6 +473,30 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { this._entityId = automation?.entity_id; } + private async _checkValidation() { + this._validationErrors = undefined; + if (!this._entityId || !this._config) { + return; + } + const stateObj = this.hass.states[this._entityId]; + if (stateObj?.state !== UNAVAILABLE) { + return; + } + const validation = await validateConfig(this.hass, { + trigger: this._config.trigger, + condition: this._config.condition, + action: this._config.action, + }); + this._validationErrors = Object.entries(validation).map(([key, value]) => + value.valid + ? "" + : html`${this.hass.localize( + `ui.panel.config.automation.editor.${key}s.header` + )}: + ${value.error}
` + ); + } + private _normalizeConfig(config: AutomationConfig): AutomationConfig { // Normalize data: ensure trigger, action and condition are lists // Happens when people copy paste their automations into the config @@ -476,6 +518,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { this._dirty = false; this._readOnly = false; this._config = this._normalizeConfig(config); + this._checkValidation(); } catch (err: any) { const entityRegistry = await fetchEntityRegistry(this.hass.connection); const entity = entityRegistry.find( @@ -686,6 +729,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { await this._promptAutomationAlias(); } + this._validationErrors = undefined; try { await saveAutomationConfig(this.hass, id, this._config!); } catch (errors: any) { diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index eb1003aa0b..65af78bae3 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -15,6 +15,7 @@ import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { differenceInDays } from "date-fns/esm"; +import { styleMap } from "lit/directives/style-map"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { formatShortDateTime } from "../../../common/datetime/format_date_time"; import { relativeTime } from "../../../common/datetime/relative_time"; @@ -52,6 +53,7 @@ import { configSections } from "../ha-panel-config"; import { showNewAutomationDialog } from "./show-dialog-new-automation"; import { findRelated } from "../../../data/search"; import { fetchBlueprints } from "../../../data/blueprint"; +import { UNAVAILABLE } from "../../../data/entity"; @customElement("ha-automation-picker") class HaAutomationPicker extends LitElement { @@ -106,7 +108,15 @@ class HaAutomationPicker extends LitElement { ), type: "icon", template: (_, automation) => - html``, + html``, }, name: { title: this.hass.localize(