diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-state.ts b/src/panels/config/automation/condition/types/ha-automation-condition-state.ts index 03c9ce25c0..5df15283b7 100644 --- a/src/panels/config/automation/condition/types/ha-automation-condition-state.ts +++ b/src/panels/config/automation/condition/types/ha-automation-condition-state.ts @@ -1,17 +1,27 @@ import "@polymer/paper-input/paper-input"; import { html, LitElement, PropertyValues } from "lit"; import { customElement, property } from "lit/decorators"; +import { assert, literal, object, optional, string, union } from "superstruct"; import { createDurationData } from "../../../../../common/datetime/create_duration_data"; +import { fireEvent } from "../../../../../common/dom/fire_event"; import "../../../../../components/entity/ha-entity-attribute-picker"; import "../../../../../components/entity/ha-entity-picker"; +import "../../../../../components/ha-duration-input"; import { StateCondition } from "../../../../../data/automation"; import { HomeAssistant } from "../../../../../types"; +import { forDictStruct } from "../../structs"; import { ConditionElement, handleChangeEvent, } from "../ha-automation-condition-row"; -import "../../../../../components/ha-duration-input"; -import { fireEvent } from "../../../../../common/dom/fire_event"; + +const stateConditionStruct = object({ + condition: literal("state"), + entity_id: string(), + attribute: optional(string()), + state: string(), + for: optional(union([string(), forDictStruct])), +}); @customElement("ha-automation-condition-state") export class HaStateCondition extends LitElement implements ConditionElement { @@ -23,19 +33,14 @@ export class HaStateCondition extends LitElement implements ConditionElement { return { entity_id: "", state: "" }; } - public willUpdate(changedProperties: PropertyValues): boolean { - if ( - changedProperties.has("condition") && - Array.isArray(this.condition?.state) - ) { - fireEvent( - this, - "ui-mode-not-available", - Error(this.hass.localize("ui.errors.config.no_state_array_support")) - ); - // We have to stop the update if state is an array. - // Otherwise the state will be changed to a comma-separated string by the input element. - return false; + public shouldUpdate(changedProperties: PropertyValues) { + if (changedProperties.has("condition")) { + try { + assert(this.condition, stateConditionStruct); + } catch (e: any) { + fireEvent(this, "ui-mode-not-available", e); + return false; + } } return true; } diff --git a/src/panels/config/automation/structs.ts b/src/panels/config/automation/structs.ts new file mode 100644 index 0000000000..eb6baefa97 --- /dev/null +++ b/src/panels/config/automation/structs.ts @@ -0,0 +1,8 @@ +import { object, optional, number } from "superstruct"; + +export const forDictStruct = object({ + days: optional(number()), + hours: optional(number()), + minutes: optional(number()), + seconds: optional(number()), +}); diff --git a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts index 3e8a6bee37..7db893f8fe 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -68,7 +68,7 @@ export const handleChangeEvent = (element: TriggerElement, ev: CustomEvent) => { } let newTrigger: Trigger; - if (!newVal) { + if (newVal === undefined || newVal === "") { newTrigger = { ...element.trigger }; delete newTrigger[name]; } else { diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts index 6be2d30fab..e75f2f8b33 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts @@ -1,19 +1,30 @@ import "@polymer/paper-input/paper-input"; import { html, LitElement, PropertyValues } from "lit"; import { customElement, property } from "lit/decorators"; +import { assert, literal, object, optional, string, union } from "superstruct"; import { createDurationData } from "../../../../../common/datetime/create_duration_data"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { hasTemplate } from "../../../../../common/string/has-template"; import "../../../../../components/entity/ha-entity-attribute-picker"; import "../../../../../components/entity/ha-entity-picker"; +import "../../../../../components/ha-duration-input"; import { StateTrigger } from "../../../../../data/automation"; import { HomeAssistant } from "../../../../../types"; -import "../../../../../components/ha-duration-input"; +import { forDictStruct } from "../../structs"; import { handleChangeEvent, TriggerElement, } from "../ha-automation-trigger-row"; +const stateTriggerStruct = object({ + platform: literal("state"), + entity_id: string(), + attribute: optional(string()), + from: optional(string()), + to: optional(string()), + for: optional(union([string(), forDictStruct])), +}); + @customElement("ha-automation-trigger-state") export class HaStateTrigger extends LitElement implements TriggerElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -24,9 +35,9 @@ export class HaStateTrigger extends LitElement implements TriggerElement { return { entity_id: "" }; } - public willUpdate(changedProperties: PropertyValues) { + public shouldUpdate(changedProperties: PropertyValues) { if (!changedProperties.has("trigger")) { - return; + return true; } // Check for templates in trigger. If found, revert to YAML mode. if (this.trigger && hasTemplate(this.trigger)) { @@ -35,7 +46,15 @@ export class HaStateTrigger extends LitElement implements TriggerElement { "ui-mode-not-available", Error(this.hass.localize("ui.errors.config.no_template_editor_support")) ); + return false; } + try { + assert(this.trigger, stateTriggerStruct); + } catch (e: any) { + fireEvent(this, "ui-mode-not-available", e); + return false; + } + return true; } protected render() { diff --git a/src/resources/styles.ts b/src/resources/styles.ts index 5c4cc273c6..150e5efede 100644 --- a/src/resources/styles.ts +++ b/src/resources/styles.ts @@ -95,6 +95,7 @@ export const derivedStyles = { "mdc-theme-text-disabled-on-light": "var(--disabled-text-color)", "mdc-theme-text-primary-on-background": "var(--primary-text-color)", "mdc-theme-text-secondary-on-background": "var(--secondary-text-color)", + "mdc-theme-text-hint-on-background": "var(--secondary-text-color)", "mdc-theme-text-icon-on-background": "var(--secondary-text-color)", "mdc-theme-error": "var(--error-color)", "app-header-text-color": "var(--text-primary-color)", diff --git a/src/translations/en.json b/src/translations/en.json index ab73f2fc4c..5c25d8c494 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -878,8 +878,7 @@ "key_missing": "Required key ''{key}'' is missing.", "key_not_expected": "Key ''{key}'' is not expected or not supported by the visual editor.", "key_wrong_type": "The provided value for ''{key}'' is not supported by the visual editor. We support ({type_correct}) but received ({type_wrong}).", - "no_template_editor_support": "Templates not supported in visual editor", - "no_state_array_support": "Multiple state values not supported in visual editor" + "no_template_editor_support": "Templates not supported in visual editor" }, "supervisor": { "title": "Could not load the Supervisor panel!",