Automation Conditions to conversion to ha-form or mwc (#11727)

This commit is contained in:
Zack Barett 2022-02-18 14:48:17 -06:00 committed by GitHub
parent cc177ef911
commit 494cc3a569
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 452 additions and 499 deletions

View File

@ -49,7 +49,7 @@ export interface HaFormSelectSchema extends HaFormBaseSchema {
export interface HaFormMultiSelectSchema extends HaFormBaseSchema { export interface HaFormMultiSelectSchema extends HaFormBaseSchema {
type: "multi_select"; type: "multi_select";
options: Record<string, string> | string[]; options: Record<string, string> | string[] | Array<[string, string]>;
} }
export interface HaFormFloatSchema extends HaFormBaseSchema { export interface HaFormFloatSchema extends HaFormBaseSchema {

View File

@ -22,6 +22,7 @@ export class HaTimeSelector extends LitElement {
.value=${this.value} .value=${this.value}
.locale=${this.hass.locale} .locale=${this.hass.locale}
.disabled=${this.disabled} .disabled=${this.disabled}
.label=${this.label}
enable-second enable-second
></ha-time-input> ></ha-time-input>
`; `;

View File

@ -6,7 +6,7 @@ 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 { stringCompare } from "../../../../common/string/compare"; import { stringCompare } from "../../../../common/string/compare";
import { LocalizeFunc } from "../../../../common/translations/localize"; import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-card"; import "../../../../components/ha-card";
import "../../../../components/ha-yaml-editor"; import "../../../../components/ha-yaml-editor";
import type { Condition } from "../../../../data/automation"; import type { Condition } from "../../../../data/automation";

View File

@ -27,7 +27,7 @@ export const handleChangeEvent = (
if (!name) { if (!name) {
return; return;
} }
const newVal = ev.detail.value; const newVal = ev.detail?.value || (ev.currentTarget as any)?.value;
if ((element.condition[name] || "") === newVal) { if ((element.condition[name] || "") === newVal) {
return; return;

View File

@ -1,4 +1,4 @@
import { html, LitElement } from "lit"; import { css, html, LitElement } 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 { fireEvent } from "../../../../../common/dom/fire_event"; import { fireEvent } from "../../../../../common/dom/fire_event";
@ -11,7 +11,7 @@ import {
DeviceCondition, DeviceCondition,
fetchDeviceConditionCapabilities, fetchDeviceConditionCapabilities,
} from "../../../../../data/device_automation"; } from "../../../../../data/device_automation";
import { HomeAssistant } from "../../../../../types"; import type { HomeAssistant } from "../../../../../types";
@customElement("ha-automation-condition-device") @customElement("ha-automation-condition-device")
export class HaDeviceCondition extends LitElement { export class HaDeviceCondition extends LitElement {
@ -147,6 +147,13 @@ export class HaDeviceCondition extends LitElement {
`ui.panel.config.automation.editor.conditions.type.device.extra_fields.${schema.name}` `ui.panel.config.automation.editor.conditions.type.device.extra_fields.${schema.name}`
) || schema.name; ) || schema.name;
} }
static styles = css`
ha-device-picker {
display: block;
margin-bottom: 24px;
}
`;
} }
declare global { declare global {

View File

@ -1,17 +1,20 @@
import { html, LitElement } from "lit"; import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event"; import { fireEvent } from "../../../../../common/dom/fire_event";
import { Condition, LogicalCondition } from "../../../../../data/automation"; import type {
import { HomeAssistant } from "../../../../../types"; Condition,
LogicalCondition,
} from "../../../../../data/automation";
import type { HomeAssistant } from "../../../../../types";
import "../ha-automation-condition"; import "../ha-automation-condition";
import { ConditionElement } from "../ha-automation-condition-row"; import type { ConditionElement } from "../ha-automation-condition-row";
import { HaStateCondition } from "./ha-automation-condition-state"; import { HaStateCondition } from "./ha-automation-condition-state";
@customElement("ha-automation-condition-logical") @customElement("ha-automation-condition-logical")
export class HaLogicalCondition extends LitElement implements ConditionElement { export class HaLogicalCondition extends LitElement implements ConditionElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() public condition!: LogicalCondition; @property({ attribute: false }) public condition!: LogicalCondition;
public static get defaultConfig() { public static get defaultConfig() {
return { return {

View File

@ -1,17 +1,17 @@
import "@polymer/paper-input/paper-input"; import "../../../../../components/ha-form/ha-form";
import "@polymer/paper-input/paper-textarea";
import { html, LitElement } from "lit"; import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import "../../../../../components/entity/ha-entity-picker"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../../common/dom/fire_event";
import type { HaFormSchema } from "../../../../../components/ha-form/types";
import { NumericStateCondition } from "../../../../../data/automation"; import { NumericStateCondition } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types"; import type { HomeAssistant } from "../../../../../types";
import { handleChangeEvent } from "../ha-automation-condition-row";
@customElement("ha-automation-condition-numeric_state") @customElement("ha-automation-condition-numeric_state")
export default class HaNumericStateCondition extends LitElement { export default class HaNumericStateCondition extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() public condition!: NumericStateCondition; @property({ attribute: false }) public condition!: NumericStateCondition;
public static get defaultConfig() { public static get defaultConfig() {
return { return {
@ -19,60 +19,54 @@ export default class HaNumericStateCondition extends LitElement {
}; };
} }
private _schema = memoizeOne((entityId): HaFormSchema[] => [
{ name: "entity_id", required: true, selector: { entity: {} } },
{
name: "attribute",
selector: { attribute: { entity_id: entityId } },
},
{ name: "above", selector: { text: {} } },
{ name: "below", selector: { text: {} } },
{
name: "value_template",
selector: { text: { multiline: true } },
},
]);
public render() { public render() {
const { value_template, entity_id, attribute, below, above } = const schema = this._schema(this.condition.entity_id);
this.condition;
return html` return html`
<ha-entity-picker <ha-form
.value=${entity_id}
.name=${"entity_id"}
@value-changed=${this._valueChanged}
.hass=${this.hass} .hass=${this.hass}
allow-custom-entity .data=${this.condition}
></ha-entity-picker> .schema=${schema}
<ha-entity-attribute-picker
.hass=${this.hass}
.entityId=${entity_id}
.value=${attribute}
.name=${"attribute"}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.state.attribute"
)}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
allow-custom-value .computeLabel=${this._computeLabelCallback}
></ha-entity-attribute-picker> ></ha-form>
<paper-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.numeric_state.above"
)}
name="above"
.value=${above}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.numeric_state.below"
)}
name="below"
.value=${below}
@value-changed=${this._valueChanged}
></paper-input>
<paper-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.numeric_state.value_template"
)}
name="value_template"
.value=${value_template}
@value-changed=${this._valueChanged}
dir="ltr"
></paper-textarea>
`; `;
} }
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent): void {
handleChangeEvent(this, ev); ev.stopPropagation();
const newTrigger = ev.detail.value;
fireEvent(this, "value-changed", { value: newTrigger });
} }
private _computeLabelCallback = (schema: HaFormSchema): string => {
switch (schema.name) {
case "entity_id":
return this.hass.localize("ui.components.entity.entity-picker.entity");
case "attribute":
return this.hass.localize(
"ui.components.entity.entity-attribute-picker.attribute"
);
default:
return this.hass.localize(
`ui.panel.config.automation.editor.triggers.type.numeric_state.${schema.name}`
);
}
};
} }
declare global { declare global {

View File

@ -1,19 +1,14 @@
import "@polymer/paper-input/paper-input";
import { html, LitElement, PropertyValues } from "lit"; import { html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { assert, literal, object, optional, string, union } from "superstruct"; import { assert, literal, object, optional, string, union } from "superstruct";
import { createDurationData } from "../../../../../common/datetime/create_duration_data"; import { createDurationData } from "../../../../../common/datetime/create_duration_data";
import { fireEvent } from "../../../../../common/dom/fire_event"; import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/entity/ha-entity-attribute-picker"; import type { HaFormSchema } from "../../../../../components/ha-form/types";
import "../../../../../components/entity/ha-entity-picker"; import type { StateCondition } from "../../../../../data/automation";
import "../../../../../components/ha-duration-input"; import type { HomeAssistant } from "../../../../../types";
import { StateCondition } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
import { forDictStruct } from "../../structs"; import { forDictStruct } from "../../structs";
import { import type { ConditionElement } from "../ha-automation-condition-row";
ConditionElement,
handleChangeEvent,
} from "../ha-automation-condition-row";
const stateConditionStruct = object({ const stateConditionStruct = object({
condition: literal("state"), condition: literal("state"),
@ -27,12 +22,22 @@ const stateConditionStruct = object({
export class HaStateCondition extends LitElement implements ConditionElement { export class HaStateCondition extends LitElement implements ConditionElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() public condition!: StateCondition; @property({ attribute: false }) public condition!: StateCondition;
public static get defaultConfig() { public static get defaultConfig() {
return { entity_id: "", state: "" }; return { entity_id: "", state: "" };
} }
private _schema = memoizeOne((entityId) => [
{ name: "entity_id", required: true, selector: { entity: {} } },
{
name: "attribute",
selector: { attribute: { entity_id: entityId } },
},
{ name: "state", selector: { text: {} } },
{ name: "for", selector: { duration: {} } },
]);
public shouldUpdate(changedProperties: PropertyValues) { public shouldUpdate(changedProperties: PropertyValues) {
if (changedProperties.has("condition")) { if (changedProperties.has("condition")) {
try { try {
@ -46,50 +51,52 @@ export class HaStateCondition extends LitElement implements ConditionElement {
} }
protected render() { protected render() {
const { entity_id, attribute, state } = this.condition; const trgFor = createDurationData(this.condition.for);
const forTime = createDurationData(this.condition.for); const data = { ...this.condition, ...{ for: trgFor } };
const schema = this._schema(this.condition.entity_id);
return html` return html`
<ha-entity-picker <ha-form
.value=${entity_id}
.name=${"entity_id"}
@value-changed=${this._valueChanged}
.hass=${this.hass} .hass=${this.hass}
allow-custom-entity .data=${data}
></ha-entity-picker> .schema=${schema}
<ha-entity-attribute-picker
.hass=${this.hass}
.entityId=${entity_id}
.value=${attribute}
.name=${"attribute"}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.state.attribute"
)}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
allow-custom-value .computeLabel=${this._computeLabelCallback}
></ha-entity-attribute-picker> ></ha-form>
<paper-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.state.state"
)}
.name=${"state"}
.value=${state}
@value-changed=${this._valueChanged}
></paper-input>
<ha-duration-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.state.for"
)}
.name=${"for"}
.data=${forTime}
@value-changed=${this._valueChanged}
></ha-duration-input>
`; `;
} }
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent): void {
handleChangeEvent(this, ev); ev.stopPropagation();
const newTrigger = ev.detail.value;
Object.keys(newTrigger).forEach((key) =>
newTrigger[key] === undefined || newTrigger[key] === ""
? delete newTrigger[key]
: {}
);
fireEvent(this, "value-changed", { value: newTrigger });
} }
private _computeLabelCallback = (schema: HaFormSchema): string => {
switch (schema.name) {
case "entity_id":
return this.hass.localize("ui.components.entity.entity-picker.entity");
case "attribute":
return this.hass.localize(
"ui.components.entity.entity-attribute-picker.attribute"
);
case "for":
return this.hass.localize(
`ui.panel.config.automation.editor.triggers.type.state.for`
);
default:
return this.hass.localize(
`ui.panel.config.automation.editor.conditions.type.state.${schema.name}`
);
}
};
} }
declare global { declare global {

View File

@ -1,16 +1,12 @@
import "@polymer/paper-input/paper-input"; import { html, LitElement } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../../common/dom/fire_event"; import { fireEvent } from "../../../../../common/dom/fire_event";
import type { SunCondition } from "../../../../../data/automation"; import type { SunCondition } from "../../../../../data/automation";
import type { HomeAssistant } from "../../../../../types"; import type { HomeAssistant } from "../../../../../types";
import { import type { ConditionElement } from "../ha-automation-condition-row";
ConditionElement, import type { LocalizeFunc } from "../../../../../common/translations/localize";
handleChangeEvent, import type { HaFormSchema } from "../../../../../components/ha-form/types";
} from "../ha-automation-condition-row";
import "../../../../../components/ha-radio";
import "../../../../../components/ha-formfield";
import type { HaRadio } from "../../../../../components/ha-radio";
@customElement("ha-automation-condition-sun") @customElement("ha-automation-condition-sun")
export class HaSunCondition extends LitElement implements ConditionElement { export class HaSunCondition extends LitElement implements ConditionElement {
@ -22,111 +18,72 @@ export class HaSunCondition extends LitElement implements ConditionElement {
return {}; return {};
} }
private _schema = memoizeOne((localize: LocalizeFunc) => [
{
name: "before",
type: "select",
required: true,
options: [
[
"sunrise",
localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunrise"
),
],
[
"sunset",
localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunset"
),
],
],
},
{ name: "before_offset", selector: { text: {} } },
{
name: "after",
type: "select",
required: true,
options: [
[
"sunrise",
localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunrise"
),
],
[
"sunset",
localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunset"
),
],
],
},
{ name: "after_offset", selector: { text: {} } },
]);
protected render() { protected render() {
const { after, after_offset, before, before_offset } = this.condition; const schema = this._schema(this.hass.localize);
return html` return html`
<label> <ha-form
${this.hass.localize( .schema=${schema}
"ui.panel.config.automation.editor.conditions.type.sun.before" .data=${this.condition}
)} .hass=${this.hass}
<ha-formfield .computeLabel=${this._computeLabelCallback}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunrise"
)}
>
<ha-radio
name="before"
value="sunrise"
.checked=${before === "sunrise"}
@change=${this._radioGroupPicked}
></ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunset"
)}
>
<ha-radio
name="before"
value="sunset"
.checked=${before === "sunset"}
@change=${this._radioGroupPicked}
></ha-radio>
</ha-formfield>
</label>
<paper-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.sun.before_offset"
)}
name="before_offset"
.value=${before_offset}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></paper-input> ></ha-form>
<label>
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.sun.after"
)}
<ha-formfield
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunrise"
)}
>
<ha-radio
name="after"
value="sunrise"
.checked=${after === "sunrise"}
@change=${this._radioGroupPicked}
></ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunset"
)}
>
<ha-radio
name="after"
value="sunset"
.checked=${after === "sunset"}
@change=${this._radioGroupPicked}
></ha-radio>
</ha-formfield>
</label>
<paper-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.sun.after_offset"
)}
name="after_offset"
.value=${after_offset}
@value-changed=${this._valueChanged}
></paper-input>
`; `;
} }
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent): void {
handleChangeEvent(this, ev);
}
private _radioGroupPicked(ev: CustomEvent) {
const key = (ev.target as HaRadio).name;
ev.stopPropagation(); ev.stopPropagation();
fireEvent(this, "value-changed", { const newTrigger = ev.detail.value;
value: { fireEvent(this, "value-changed", { value: newTrigger });
...this.condition,
[key]: (ev.target as HaRadio).value,
},
});
} }
static styles = css` private _computeLabelCallback = (schema: HaFormSchema): string =>
label { this.hass.localize(
display: flex; `ui.panel.config.automation.editor.conditions.type.sun.${schema.name}`
align-items: center; );
}
`;
} }
declare global { declare global {

View File

@ -1,15 +1,15 @@
import "@polymer/paper-input/paper-textarea"; import "../../../../../components/ha-textarea";
import { html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { TemplateCondition } from "../../../../../data/automation"; import type { TemplateCondition } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types"; import type { HomeAssistant } from "../../../../../types";
import { handleChangeEvent } from "../ha-automation-condition-row"; import { handleChangeEvent } from "../ha-automation-condition-row";
@customElement("ha-automation-condition-template") @customElement("ha-automation-condition-template")
export class HaTemplateCondition extends LitElement { export class HaTemplateCondition extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() public condition!: TemplateCondition; @property({ attribute: false }) public condition!: TemplateCondition;
public static get defaultConfig() { public static get defaultConfig() {
return { value_template: "" }; return { value_template: "" };
@ -18,19 +18,32 @@ export class HaTemplateCondition extends LitElement {
protected render() { protected render() {
const { value_template } = this.condition; const { value_template } = this.condition;
return html` return html`
<paper-textarea <ha-textarea
name="value_template"
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.template.value_template" "ui.panel.config.automation.editor.conditions.type.template.value_template"
)} )}
name="value_template"
.value=${value_template} .value=${value_template}
@value-changed=${this._valueChanged} @input=${this._valueChanged}
dir="ltr" dir="ltr"
></paper-textarea> autogrow
></ha-textarea>
`; `;
} }
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent): void {
handleChangeEvent(this, ev); handleChangeEvent(this, ev);
} }
static styles = css`
ha-textarea {
display: block;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-automation-condition-template": HaTemplateCondition;
}
} }

View File

@ -1,20 +1,12 @@
import { Radio } from "@material/mwc-radio"; import { html, LitElement } from "lit";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../../common/dom/fire_event"; import { fireEvent } from "../../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../../common/util/compute_rtl"; import type { TimeCondition } from "../../../../../data/automation";
import "../../../../../components/ha-formfield"; import type { HomeAssistant } from "../../../../../types";
import "../../../../../components/ha-radio"; import type { ConditionElement } from "../ha-automation-condition-row";
import { HaSwitch } from "../../../../../components/ha-switch"; import type { LocalizeFunc } from "../../../../../common/translations/localize";
import { TimeCondition } from "../../../../../data/automation"; import type { HaFormSchema } from "../../../../../components/ha-form/types";
import { HomeAssistant } from "../../../../../types";
import {
ConditionElement,
handleChangeEvent,
} from "../ha-automation-condition-row";
import "../../../../../components/ha-time-input";
const includeDomains = ["input_datetime"];
const DAYS = { const DAYS = {
mon: 1, mon: 1,
@ -26,10 +18,6 @@ const DAYS = {
sun: 7, sun: 7,
}; };
interface WeekdayHaSwitch extends HaSwitch {
day: string;
}
@customElement("ha-automation-condition-time") @customElement("ha-automation-condition-time")
export class HaTimeCondition extends LitElement implements ConditionElement { export class HaTimeCondition extends LitElement implements ConditionElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -44,176 +32,136 @@ export class HaTimeCondition extends LitElement implements ConditionElement {
return {}; return {};
} }
protected render() { private _schema = memoizeOne(
const { after, before, weekday } = this.condition; (
localize: LocalizeFunc,
inputModeAfter?: boolean,
inputModeBefore?: boolean
): HaFormSchema[] => {
const modeAfterSchema = inputModeAfter
? { name: "after", selector: { entity: { domain: "input_datetime" } } }
: { name: "after", selector: { time: {} } };
const modeBeforeSchema = inputModeBefore
? { name: "before", selector: { entity: { domain: "input_datetime" } } }
: { name: "before", selector: { time: {} } };
return [
{
name: "mode_after",
type: "select",
required: true,
options: [
[
"value",
localize(
"ui.panel.config.automation.editor.conditions.type.time.type_value"
),
],
[
"input",
localize(
"ui.panel.config.automation.editor.conditions.type.time.type_input"
),
],
],
},
modeAfterSchema,
{
name: "mode_before",
type: "select",
required: true,
options: [
[
"value",
localize(
"ui.panel.config.automation.editor.conditions.type.time.type_value"
),
],
[
"input",
localize(
"ui.panel.config.automation.editor.conditions.type.time.type_input"
),
],
],
},
modeBeforeSchema,
{
type: "multi_select",
name: "weekday",
options: Object.keys(DAYS).map((day) => [
day,
localize(
`ui.panel.config.automation.editor.conditions.type.time.weekdays.${day}`
),
]),
},
];
}
);
protected render() {
const inputModeBefore = const inputModeBefore =
this._inputModeBefore ?? before?.startsWith("input_datetime."); this._inputModeBefore ??
this.condition.before?.startsWith("input_datetime.");
const inputModeAfter = const inputModeAfter =
this._inputModeAfter ?? after?.startsWith("input_datetime."); this._inputModeAfter ??
this.condition.after?.startsWith("input_datetime.");
const schema: HaFormSchema[] = this._schema(
this.hass.localize,
inputModeAfter,
inputModeBefore
);
const data = {
mode_before: "value",
mode_after: "value",
...this.condition,
};
return html` return html`
<ha-formfield <ha-form
.label=${this.hass!.localize(
"ui.panel.config.automation.editor.conditions.type.time.type_value"
)}
>
<ha-radio
@change=${this._handleModeChanged}
name="mode_after"
value="value"
?checked=${!inputModeAfter}
></ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.hass!.localize(
"ui.panel.config.automation.editor.conditions.type.time.type_input"
)}
>
<ha-radio
@change=${this._handleModeChanged}
name="mode_after"
value="input"
?checked=${inputModeAfter}
></ha-radio>
</ha-formfield>
${inputModeAfter
? html`<ha-entity-picker
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.time.after"
)}
.includeDomains=${includeDomains}
.name=${"after"}
.value=${after?.startsWith("input_datetime.") ? after : ""}
@value-changed=${this._valueChanged}
.hass=${this.hass} .hass=${this.hass}
allow-custom-entity .data=${data}
></ha-entity-picker>` .schema=${schema}
: html`<ha-time-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.time.after"
)}
.locale=${this.hass.locale}
.name=${"after"}
.value=${after?.startsWith("input_datetime.") ? "" : after}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></ha-time-input>`} .computeLabel=${this._computeLabelCallback}
></ha-form>
<ha-formfield
.label=${this.hass!.localize(
"ui.panel.config.automation.editor.conditions.type.time.type_value"
)}
>
<ha-radio
@change=${this._handleModeChanged}
name="mode_before"
value="value"
?checked=${!inputModeBefore}
></ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.hass!.localize(
"ui.panel.config.automation.editor.conditions.type.time.type_input"
)}
>
<ha-radio
@change=${this._handleModeChanged}
name="mode_before"
value="input"
?checked=${inputModeBefore}
></ha-radio>
</ha-formfield>
${inputModeBefore
? html`<ha-entity-picker
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.time.before"
)}
.includeDomains=${includeDomains}
.name=${"before"}
.value=${before?.startsWith("input_datetime.") ? before : ""}
@value-changed=${this._valueChanged}
.hass=${this.hass}
allow-custom-entity
></ha-entity-picker>`
: html`<ha-time-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.time.before"
)}
.name=${"before"}
.locale=${this.hass.locale}
.value=${before?.startsWith("input_datetime.") ? "" : before}
@value-changed=${this._valueChanged}
></ha-time-input>`}
${Object.keys(DAYS).map(
(day) => html`
<ha-formfield
alignEnd
spaceBetween
class="weekday-toggle"
.label=${this.hass!.localize(
`ui.panel.config.automation.editor.conditions.type.time.weekdays.${day}`
)}
.dir=${computeRTLDirection(this.hass!)}
>
<ha-switch
.day=${day}
.checked=${!weekday || weekday === day || weekday.includes(day)}
@change=${this._dayValueChanged}
>
</ha-switch>
</ha-formfield>
`
)}
`; `;
} }
private _handleModeChanged(ev: Event) {
const target = ev.target as Radio;
if (target.getAttribute("name") === "mode_after") {
this._inputModeAfter = target.value === "input";
} else {
this._inputModeBefore = target.value === "input";
}
}
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent): void {
handleChangeEvent(this, ev); ev.stopPropagation();
const newValue = ev.detail.value;
const newModeAfter = newValue.mode_after === "input";
const newModeBefore = newValue.mode_before === "input";
if (newModeAfter !== this._inputModeAfter) {
this._inputModeAfter = newModeAfter;
newValue.after = undefined;
} }
private _dayValueChanged(ev: CustomEvent): void { if (newModeBefore !== this._inputModeBefore) {
const daySwitch = ev.currentTarget as WeekdayHaSwitch; this._inputModeBefore = newModeBefore;
newValue.before = undefined;
let days: string[];
if (!this.condition.weekday) {
days = Object.keys(DAYS);
} else {
days = !Array.isArray(this.condition.weekday)
? [this.condition.weekday]
: this.condition.weekday;
} }
if (daySwitch.checked) { Object.keys(newValue).forEach((key) =>
days.push(daySwitch.day); newValue[key] === undefined || newValue[key] === ""
} else { ? delete newValue[key]
days = days.filter((d) => d !== daySwitch.day); : {}
);
fireEvent(this, "value-changed", { value: newValue });
} }
days.sort((a: string, b: string) => DAYS[a] - DAYS[b]); private _computeLabelCallback = (schema: HaFormSchema): string =>
this.hass.localize(
fireEvent(this, "value-changed", { `ui.panel.config.automation.editor.conditions.type.time.${schema.name}`
value: { ...this.condition, weekday: days }, );
});
}
static get styles(): CSSResultGroup {
return css`
.weekday-toggle {
display: flex;
height: 40px;
}
`;
}
} }
declare global { declare global {

View File

@ -5,12 +5,12 @@ import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event"; import { fireEvent } from "../../../../../common/dom/fire_event";
import { ensureArray } from "../../../../../common/ensure-array"; import { ensureArray } from "../../../../../common/ensure-array";
import { import type {
AutomationConfig, AutomationConfig,
Trigger, Trigger,
TriggerCondition, TriggerCondition,
} from "../../../../../data/automation"; } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types"; import type { HomeAssistant } from "../../../../../types";
@customElement("ha-automation-condition-trigger") @customElement("ha-automation-condition-trigger")
export class HaTriggerCondition extends LitElement { export class HaTriggerCondition extends LitElement {

View File

@ -1,4 +1,4 @@
import { html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event"; import { fireEvent } from "../../../../../common/dom/fire_event";
import { computeStateDomain } from "../../../../../common/entity/compute_state_domain"; import { computeStateDomain } from "../../../../../common/entity/compute_state_domain";
@ -71,6 +71,13 @@ export class HaZoneCondition extends LitElement {
value: { ...this.condition, zone: ev.detail.value }, value: { ...this.condition, zone: ev.detail.value },
}); });
} }
static styles = css`
ha-entity-picker {
display: block;
margin-bottom: 24px;
}
`;
} }
declare global { declare global {

View File

@ -154,7 +154,7 @@ export class HaDeviceTrigger extends LitElement {
static styles = css` static styles = css`
ha-device-picker { ha-device-picker {
display: block; display: block;
margin-bottom: 8px; margin-bottom: 24px;
} }
`; `;
} }

View File

@ -16,19 +16,18 @@ export class HaNumericStateTrigger extends LitElement {
@property() public trigger!: NumericStateTrigger; @property() public trigger!: NumericStateTrigger;
private _schema = memoizeOne((entityId): HaFormSchema[] => [ private _schema = memoizeOne((entityId): HaFormSchema[] => [
{ name: "entity_id", selector: { entity: {} } }, { name: "entity_id", required: true, selector: { entity: {} } },
{ {
name: "attribute", name: "attribute",
selector: { attribute: { entity_id: entityId } }, selector: { attribute: { entity_id: entityId } },
}, },
{ name: "above", required: false, selector: { text: {} } }, { name: "above", selector: { text: {} } },
{ name: "below", required: false, selector: { text: {} } }, { name: "below", selector: { text: {} } },
{ {
name: "value_template", name: "value_template",
required: false,
selector: { text: { multiline: true } }, selector: { text: { multiline: true } },
}, },
{ name: "for", required: false, selector: { duration: {} } }, { name: "for", selector: { duration: {} } },
]); ]);
public willUpdate(changedProperties: PropertyValues) { public willUpdate(changedProperties: PropertyValues) {

View File

@ -32,14 +32,6 @@ const stateTriggerStruct = assign(
}) })
); );
const SCHEMA = [
{ name: "entity_id", selector: { entity: {} } },
{ name: "attribute", selector: { attribute: { entity_id: "" } } },
{ name: "from", required: false, selector: { text: {} } },
{ name: "to", required: false, selector: { text: {} } },
{ name: "for", required: false, selector: { duration: {} } },
];
@customElement("ha-automation-trigger-state") @customElement("ha-automation-trigger-state")
export class HaStateTrigger extends LitElement implements TriggerElement { export class HaStateTrigger extends LitElement implements TriggerElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -50,14 +42,16 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
return { entity_id: "" }; return { entity_id: "" };
} }
private _schema = memoizeOne((entityId) => { private _schema = memoizeOne((entityId) => [
const schema = [...SCHEMA]; { name: "entity_id", required: true, selector: { entity: {} } },
schema[1] = { {
name: "attribute", name: "attribute",
selector: { attribute: { entity_id: entityId } }, selector: { attribute: { entity_id: entityId } },
}; },
return schema; { name: "from", selector: { text: {} } },
}); { name: "to", selector: { text: {} } },
{ name: "for", selector: { duration: {} } },
]);
public shouldUpdate(changedProperties: PropertyValues) { public shouldUpdate(changedProperties: PropertyValues) {
if (!changedProperties.has("trigger")) { if (!changedProperties.has("trigger")) {
@ -118,13 +112,12 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
fireEvent(this, "value-changed", { value: newTrigger }); fireEvent(this, "value-changed", { value: newTrigger });
} }
private _computeLabelCallback(schema: HaFormSchema): string { private _computeLabelCallback = (schema: HaFormSchema): string =>
return this.hass.localize( this.hass.localize(
schema.name === "entity_id" schema.name === "entity_id"
? "ui.components.entity.entity-picker.entity" ? "ui.components.entity.entity-picker.entity"
: `ui.panel.config.automation.editor.triggers.type.state.${schema.name}` : `ui.panel.config.automation.editor.triggers.type.state.${schema.name}`
); );
}
} }
declare global { declare global {

View File

@ -45,9 +45,10 @@ export class HaSunTrigger extends LitElement implements TriggerElement {
} }
protected render() { protected render() {
const schema = this._schema(this.hass.localize);
return html` return html`
<ha-form <ha-form
.schema=${this._schema(this.hass.localize)} .schema=${schema}
.data=${this.trigger} .data=${this.trigger}
.hass=${this.hass} .hass=${this.hass}
.computeLabel=${this._computeLabelCallback} .computeLabel=${this._computeLabelCallback}

View File

@ -1,15 +1,15 @@
import "@polymer/paper-input/paper-textarea"; import "../../../../../components/ha-textarea";
import { html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { TemplateTrigger } from "../../../../../data/automation"; import type { TemplateTrigger } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types"; import type { HomeAssistant } from "../../../../../types";
import { handleChangeEvent } from "../ha-automation-trigger-row"; import { handleChangeEvent } from "../ha-automation-trigger-row";
@customElement("ha-automation-trigger-template") @customElement("ha-automation-trigger-template")
export class HaTemplateTrigger extends LitElement { export class HaTemplateTrigger extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() public trigger!: TemplateTrigger; @property({ attribute: false }) public trigger!: TemplateTrigger;
public static get defaultConfig() { public static get defaultConfig() {
return { value_template: "" }; return { value_template: "" };
@ -18,19 +18,32 @@ export class HaTemplateTrigger extends LitElement {
protected render() { protected render() {
const { value_template } = this.trigger; const { value_template } = this.trigger;
return html` return html`
<paper-textarea <ha-textarea
name="value_template"
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.template.value_template" "ui.panel.config.automation.editor.triggers.type.template.value_template"
)} )}
name="value_template"
.value=${value_template} .value=${value_template}
@value-changed=${this._valueChanged} @input=${this._valueChanged}
dir="ltr" dir="ltr"
></paper-textarea> autogrow
></ha-textarea>
`; `;
} }
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent): void {
handleChangeEvent(this, ev); handleChangeEvent(this, ev);
} }
static styles = css`
ha-textarea {
display: block;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-automation-trigger-template": HaTemplateTrigger;
}
} }

View File

@ -1,23 +1,19 @@
import memoizeOne from "memoize-one";
import { html, LitElement, PropertyValues } from "lit"; import { html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import "../../../../../components/entity/ha-entity-picker"; import type { TimeTrigger } from "../../../../../data/automation";
import "../../../../../components/ha-formfield"; import type { HomeAssistant } from "../../../../../types";
import "../../../../../components/ha-radio"; import type { TriggerElement } from "../ha-automation-trigger-row";
import { TimeTrigger } from "../../../../../data/automation"; import type { LocalizeFunc } from "../../../../../common/translations/localize";
import { HomeAssistant } from "../../../../../types"; import type { HaFormSchema } from "../../../../../components/ha-form/types";
import {
handleChangeEvent,
TriggerElement,
} from "../ha-automation-trigger-row";
import "../../../../../components/ha-time-input";
import { fireEvent } from "../../../../../common/dom/fire_event"; import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-form/ha-form";
const includeDomains = ["input_datetime"];
@customElement("ha-automation-trigger-time") @customElement("ha-automation-trigger-time")
export class HaTimeTrigger extends LitElement implements TriggerElement { export class HaTimeTrigger extends LitElement implements TriggerElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() public trigger!: TimeTrigger; @property({ attribute: false }) public trigger!: TimeTrigger;
@state() private _inputMode?: boolean; @state() private _inputMode?: boolean;
@ -25,6 +21,37 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
return { at: "" }; return { at: "" };
} }
private _schema = memoizeOne(
(localize: LocalizeFunc, inputMode?: boolean): HaFormSchema[] => {
const modeSchema = inputMode
? { name: "at", selector: { entity: { domain: "input_datetime" } } }
: { name: "at", selector: { time: {} } };
return [
{
name: "mode",
type: "select",
required: true,
options: [
[
"value",
localize(
"ui.panel.config.automation.editor.triggers.type.time.type_value"
),
],
[
"input",
localize(
"ui.panel.config.automation.editor.triggers.type.time.type_input"
),
],
],
},
modeSchema,
];
}
);
public willUpdate(changedProperties: PropertyValues) { public willUpdate(changedProperties: PropertyValues) {
if (!changedProperties.has("trigger")) { if (!changedProperties.has("trigger")) {
return; return;
@ -50,67 +77,43 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
this._inputMode ?? this._inputMode ??
(at?.startsWith("input_datetime.") || at?.startsWith("sensor.")); (at?.startsWith("input_datetime.") || at?.startsWith("sensor."));
return html`<ha-formfield const schema: HaFormSchema[] = this._schema(this.hass.localize, inputMode);
.label=${this.hass!.localize(
"ui.panel.config.automation.editor.triggers.type.time.type_value"
)}
>
<ha-radio
@change=${this._handleModeChanged}
name="mode"
value="value"
?checked=${!inputMode}
></ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.hass!.localize(
"ui.panel.config.automation.editor.triggers.type.time.type_input"
)}
>
<ha-radio
@change=${this._handleModeChanged}
name="mode"
value="input"
?checked=${inputMode}
></ha-radio>
</ha-formfield>
${inputMode const data = {
? html`<ha-entity-picker mode: "value",
.label=${this.hass.localize( ...this.trigger,
"ui.panel.config.automation.editor.triggers.type.time.at" };
)}
.includeDomains=${includeDomains} return html`
.name=${"at"} <ha-form
.value=${at?.startsWith("input_datetime.") ||
at?.startsWith("sensor.")
? at
: ""}
@value-changed=${this._valueChanged}
.hass=${this.hass} .hass=${this.hass}
allow-custom-entity .data=${data}
></ha-entity-picker>` .schema=${schema}
: html`<ha-time-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.time.at"
)}
.name=${"at"}
.value=${at?.startsWith("input_datetime.") ||
at?.startsWith("sensor.")
? ""
: at}
.locale=${this.hass.locale}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></ha-time-input>`} `; .computeLabel=${this._computeLabelCallback}
} ></ha-form>
`;
private _handleModeChanged(ev: Event) {
this._inputMode = (ev.target as any).value === "input";
} }
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent): void {
handleChangeEvent(this, ev); ev.stopPropagation();
const newValue = ev.detail.value;
this._inputMode = newValue.mode.value === "input";
Object.keys(newValue).forEach((key) =>
newValue[key] === undefined || newValue[key] === ""
? delete newValue[key]
: {}
);
fireEvent(this, "value-changed", { value: newValue });
} }
private _computeLabelCallback = (schema: HaFormSchema): string =>
this.hass.localize(
`ui.panel.config.automation.editor.triggers.type.time.${schema.name}`
);
} }
declare global { declare global {

View File

@ -1,14 +1,13 @@
import "../../../../../components/entity/ha-entity-picker";
import "../../../../../components/ha-formfield";
import { css, html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event"; import { fireEvent } from "../../../../../common/dom/fire_event";
import { computeStateDomain } from "../../../../../common/entity/compute_state_domain"; import { computeStateDomain } from "../../../../../common/entity/compute_state_domain";
import { hasLocation } from "../../../../../common/entity/has_location"; import { hasLocation } from "../../../../../common/entity/has_location";
import "../../../../../components/entity/ha-entity-picker";
import type { ZoneTrigger } from "../../../../../data/automation"; import type { ZoneTrigger } from "../../../../../data/automation";
import type { PolymerChangedEvent } from "../../../../../polymer-types"; import type { PolymerChangedEvent } from "../../../../../polymer-types";
import type { HomeAssistant } from "../../../../../types"; import type { HomeAssistant } from "../../../../../types";
import "../../../../../components/ha-radio";
import "../../../../../components/ha-formfield";
import type { HaRadio } from "../../../../../components/ha-radio"; import type { HaRadio } from "../../../../../components/ha-radio";
function zoneAndLocationFilter(stateObj) { function zoneAndLocationFilter(stateObj) {
@ -116,6 +115,10 @@ export class HaZoneTrigger extends LitElement {
display: flex; display: flex;
align-items: center; align-items: center;
} }
ha-entity-picker {
display: block;
margin-bottom: 24px;
}
`; `;
} }

View File

@ -1653,7 +1653,7 @@
"label": "Geolocation", "label": "Geolocation",
"source": "Source", "source": "Source",
"zone": "Zone", "zone": "Zone",
"event": "Event:", "event": "Event",
"enter": "Enter", "enter": "Enter",
"leave": "Leave" "leave": "Leave"
}, },
@ -1699,7 +1699,8 @@
"type_value": "Fixed time", "type_value": "Fixed time",
"type_input": "Value of a date/time helper", "type_input": "Value of a date/time helper",
"label": "Time", "label": "Time",
"at": "At time" "at": "At time",
"mode": "Mode"
}, },
"time_pattern": { "time_pattern": {
"label": "Time Pattern", "label": "Time Pattern",
@ -1767,8 +1768,8 @@
}, },
"sun": { "sun": {
"label": "[%key:ui::panel::config::automation::editor::triggers::type::sun::label%]", "label": "[%key:ui::panel::config::automation::editor::triggers::type::sun::label%]",
"before": "Before:", "before": "Before",
"after": "After:", "after": "After",
"before_offset": "Before offset (optional)", "before_offset": "Before offset (optional)",
"after_offset": "After offset (optional)", "after_offset": "After offset (optional)",
"sunrise": "Sunrise", "sunrise": "Sunrise",
@ -1784,6 +1785,9 @@
"label": "[%key:ui::panel::config::automation::editor::triggers::type::time::label%]", "label": "[%key:ui::panel::config::automation::editor::triggers::type::time::label%]",
"after": "After", "after": "After",
"before": "Before", "before": "Before",
"weekday": "Weekdays",
"mode_after": "[%key:ui::panel::config::automation::editor::conditions::type::time::after%]",
"mode_before": "[%key:ui::panel::config::automation::editor::conditions::type::time::before%]",
"weekdays": { "weekdays": {
"mon": "Monday", "mon": "Monday",
"tue": "Tuesday", "tue": "Tuesday",