Add some Trigger Descriptions (#13460)

Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Zack Barett 2022-08-24 04:18:50 -05:00 committed by GitHub
parent 5ce81232b5
commit 807bb10199
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 354 additions and 19 deletions

View File

@ -1,25 +1,56 @@
import { dump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-yaml-editor";
import { Trigger } from "../../../../src/data/automation";
import { describeTrigger } from "../../../../src/data/automation_i18n";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types";
const ENTITIES = [
getEntity("light", "kitchen", "on", {
friendly_name: "Kitchen Light",
}),
getEntity("person", "person", "", {
friendly_name: "Person",
}),
getEntity("zone", "home", "", {
friendly_name: "Home",
}),
];
const triggers = [
{ platform: "state" },
{ platform: "state", entity_id: "light.kitchen", from: "off", to: "on" },
{ platform: "mqtt" },
{ platform: "geo_location" },
{ platform: "homeassistant" },
{ platform: "numeric_state" },
{ platform: "sun" },
{
platform: "geo_location",
source: "test_source",
zone: "zone.home",
event: "enter",
},
{ platform: "homeassistant", event: "start" },
{
platform: "numeric_state",
entity_id: "light.kitchen",
attribute: "brightness",
below: 80,
above: 20,
},
{ platform: "sun", event: "sunset" },
{ platform: "time_pattern" },
{ platform: "webhook" },
{ platform: "zone" },
{
platform: "zone",
entity_id: "person.person",
zone: "zone.home",
event: "enter",
},
{ platform: "tag" },
{ platform: "time" },
{ platform: "time", at: "15:32" },
{ platform: "template" },
{ platform: "event" },
{ platform: "event", event_type: "homeassistant_started" },
];
const initialTrigger: Trigger = {
@ -29,14 +60,22 @@ const initialTrigger: Trigger = {
@customElement("demo-automation-describe-trigger")
export class DemoAutomationDescribeTrigger extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
@state() _trigger = initialTrigger;
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
return html`
<ha-card header="Triggers">
<div class="trigger">
<span>
${this._trigger ? describeTrigger(this._trigger) : "<invalid YAML>"}
${this._trigger
? describeTrigger(this._trigger, this.hass)
: "<invalid YAML>"}
</span>
<ha-yaml-editor
label="Trigger Config"
@ -47,7 +86,7 @@ export class DemoAutomationDescribeTrigger extends LitElement {
${triggers.map(
(conf) => html`
<div class="trigger">
<span>${describeTrigger(conf as any)}</span>
<span>${describeTrigger(conf as any, this.hass)}</span>
<pre>${dump(conf)}</pre>
</div>
`
@ -56,6 +95,13 @@ export class DemoAutomationDescribeTrigger extends LitElement {
`;
}
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
const hass = provideHass(this);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
private _dataChanged(ev: CustomEvent): void {
ev.stopPropagation();
this._trigger = ev.detail.isValid ? ev.detail.value : undefined;

View File

@ -2,11 +2,296 @@ import secondsToDuration from "../common/datetime/seconds_to_duration";
import { computeStateName } from "../common/entity/compute_state_name";
import type { HomeAssistant } from "../types";
import { Condition, Trigger } from "./automation";
import { formatAttributeName } from "./entity_attributes";
export const describeTrigger = (trigger: Trigger, ignoreAlias = false) => {
export const describeTrigger = (
trigger: Trigger,
hass: HomeAssistant,
ignoreAlias = false
) => {
if (trigger.alias && !ignoreAlias) {
return trigger.alias;
}
// Event Trigger
if (trigger.platform === "event" && trigger.event_type) {
let eventTypes = "";
if (Array.isArray(trigger.event_type)) {
for (const [index, state] of trigger.event_type.entries()) {
eventTypes += `${index > 0 ? "," : ""} ${
trigger.event_type.length > 1 &&
index === trigger.event_type.length - 1
? "or"
: ""
} ${state}`;
}
} else {
eventTypes = trigger.event_type.toString();
}
return `When ${eventTypes} event is fired`;
}
// Home Assistant Trigger
if (trigger.platform === "homeassistant" && trigger.event) {
return `When Home Assistant is ${
trigger.event === "start" ? "started" : "shutdown"
}`;
}
// Numeric State Trigger
if (trigger.platform === "numeric_state" && trigger.entity_id) {
let base = "When";
const stateObj = hass.states[trigger.entity_id];
const entity = stateObj ? computeStateName(stateObj) : trigger.entity_id;
if (trigger.attribute) {
base += ` ${formatAttributeName(trigger.attribute)} from`;
}
base += ` ${entity} is`;
if ("above" in trigger) {
base += ` above ${trigger.above}`;
}
if ("below" in trigger && "above" in trigger) {
base += " and";
}
if ("below" in trigger) {
base += ` below ${trigger.below}`;
}
return base;
}
// State Trigger
if (trigger.platform === "state" && trigger.entity_id) {
let base = "When";
let entities = "";
const states = hass.states;
if (trigger.attribute) {
base += ` ${formatAttributeName(trigger.attribute)} from`;
}
if (Array.isArray(trigger.entity_id)) {
for (const [index, entity] of trigger.entity_id.entries()) {
if (states[entity]) {
entities += `${index > 0 ? "," : ""} ${
trigger.entity_id.length > 1 &&
index === trigger.entity_id.length - 1
? "or"
: ""
} ${computeStateName(states[entity]) || entity}`;
}
}
} else {
entities = states[trigger.entity_id]
? computeStateName(states[trigger.entity_id])
: trigger.entity_id;
}
base += ` ${entities} changes`;
if (trigger.from) {
let from = "";
if (Array.isArray(trigger.from)) {
for (const [index, state] of trigger.from.entries()) {
from += `${index > 0 ? "," : ""} ${
trigger.from.length > 1 && index === trigger.from.length - 1
? "or"
: ""
} ${state}`;
}
} else {
from = trigger.from.toString();
}
base += ` from ${from}`;
}
if (trigger.to) {
let to = "";
if (Array.isArray(trigger.to)) {
for (const [index, state] of trigger.to.entries()) {
to += `${index > 0 ? "," : ""} ${
trigger.to.length > 1 && index === trigger.to.length - 1 ? "or" : ""
} ${state}`;
}
} else if (trigger.to) {
to = trigger.to.toString();
}
base += ` to ${to}`;
}
if ("for" in trigger) {
let duration: string;
if (typeof trigger.for === "number") {
duration = `for ${secondsToDuration(trigger.for)!}`;
} else if (typeof trigger.for === "string") {
duration = `for ${trigger.for}`;
} else {
duration = `for ${JSON.stringify(trigger.for)}`;
}
base += ` for ${duration}`;
}
return base;
}
// Sun Trigger
if (trigger.platform === "sun" && trigger.event) {
let base = `When the sun ${trigger.event === "sunset" ? "sets" : "rises"}`;
if (trigger.offset) {
let duration = "";
if (trigger.offset) {
if (typeof trigger.offset === "number") {
duration = ` offset by ${secondsToDuration(trigger.offset)!}`;
} else if (typeof trigger.offset === "string") {
duration = ` offset by ${trigger.offset}`;
} else {
duration = ` offset by ${JSON.stringify(trigger.offset)}`;
}
}
base += duration;
}
return base;
}
// Tag Trigger
if (trigger.platform === "tag") {
return "When a tag is scanned";
}
// Time Trigger
if (trigger.platform === "time" && trigger.at) {
const at = trigger.at.includes(".")
? hass.states[trigger.at] || trigger.at
: trigger.at;
return `When the time is equal to ${at}`;
}
// Time Patter Trigger
if (trigger.platform === "time_pattern") {
return "Time pattern trigger";
}
// Zone Trigger
if (trigger.platform === "zone" && trigger.entity_id && trigger.zone) {
let entities = "";
let zones = "";
let zonesPlural = false;
const states = hass.states;
if (Array.isArray(trigger.entity_id)) {
for (const [index, entity] of trigger.entity_id.entries()) {
if (states[entity]) {
entities += `${index > 0 ? "," : ""} ${
trigger.entity_id.length > 1 &&
index === trigger.entity_id.length - 1
? "or"
: ""
} ${computeStateName(states[entity]) || entity}`;
}
}
} else {
entities = states[trigger.entity_id]
? computeStateName(states[trigger.entity_id])
: trigger.entity_id;
}
if (Array.isArray(trigger.zone)) {
if (trigger.zone.length > 1) {
zonesPlural = true;
}
for (const [index, zone] of trigger.zone.entries()) {
if (states[zone]) {
zones += `${index > 0 ? "," : ""} ${
trigger.zone.length > 1 && index === trigger.zone.length - 1
? "or"
: ""
} ${computeStateName(states[zone]) || zone}`;
}
}
} else {
zones = states[trigger.zone]
? computeStateName(states[trigger.zone])
: trigger.zone;
}
return `When ${entities} ${trigger.event}s ${zones} ${
zonesPlural ? "zones" : "zone"
}`;
}
// Geo Location Trigger
if (trigger.platform === "geo_location" && trigger.source && trigger.zone) {
let sources = "";
let zones = "";
let zonesPlural = false;
const states = hass.states;
if (Array.isArray(trigger.source)) {
for (const [index, source] of trigger.source.entries()) {
sources += `${index > 0 ? "," : ""} ${
trigger.source.length > 1 && index === trigger.source.length - 1
? "or"
: ""
} ${source}`;
}
} else {
sources = trigger.source;
}
if (Array.isArray(trigger.zone)) {
if (trigger.zone.length > 1) {
zonesPlural = true;
}
for (const [index, zone] of trigger.zone.entries()) {
if (states[zone]) {
zones += `${index > 0 ? "," : ""} ${
trigger.zone.length > 1 && index === trigger.zone.length - 1
? "or"
: ""
} ${computeStateName(states[zone]) || zone}`;
}
}
} else {
zones = states[trigger.zone]
? computeStateName(states[trigger.zone])
: trigger.zone;
}
return `When ${sources} ${trigger.event}s ${zones} ${
zonesPlural ? "zones" : "zone"
}`;
}
// MQTT Trigger
if (trigger.platform === "mqtt") {
return "When a MQTT payload has been received";
}
// Template Trigger
if (trigger.platform === "template") {
return "When a template triggers";
}
// Webhook Trigger
if (trigger.platform === "webhook") {
return "When a Webhook payload has been received";
}
return `${trigger.platform || "Unknown"} trigger`;
};

View File

@ -142,7 +142,7 @@ export const describeAction = <T extends ActionType>(
if (actionType === "wait_for_trigger") {
const config = action as WaitForTriggerAction;
return `Wait for ${ensureArray(config.wait_for_trigger)
.map((trigger) => describeTrigger(trigger))
.map((trigger) => describeTrigger(trigger, hass))
.join(", ")}`;
}

View File

@ -16,6 +16,7 @@ import { customElement, property, query, state } 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 { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
import { handleStructError } from "../../../../common/structs/handle-errors";
import { debounce } from "../../../../common/util/debounce";
import "../../../../components/ha-alert";
@ -23,9 +24,10 @@ import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
import { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import "../../../../components/ha-textfield";
import { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import { subscribeTrigger, Trigger } from "../../../../data/automation";
import { describeTrigger } from "../../../../data/automation_i18n";
import { validateConfig } from "../../../../data/config";
import {
showAlertDialog,
@ -49,8 +51,6 @@ import "./types/ha-automation-trigger-time";
import "./types/ha-automation-trigger-time_pattern";
import "./types/ha-automation-trigger-webhook";
import "./types/ha-automation-trigger-zone";
import { describeTrigger } from "../../../../data/automation_i18n";
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
export interface TriggerElement extends LitElement {
trigger: Trigger;
@ -121,7 +121,9 @@ export default class HaAutomationTriggerRow extends LitElement {
<ha-expansion-panel
leftChevron
.header=${capitalizeFirstLetter(describeTrigger(this.trigger))}
.header=${capitalizeFirstLetter(
describeTrigger(this.trigger, this.hass)
)}
>
<ha-button-menu
slot="icons"
@ -485,7 +487,9 @@ export default class HaAutomationTriggerRow extends LitElement {
"ui.panel.config.automation.editor.triggers.alias"
),
inputType: "string",
placeholder: capitalizeFirstLetter(describeTrigger(this.trigger, true)),
placeholder: capitalizeFirstLetter(
describeTrigger(this.trigger, this.hass, true)
),
defaultValue: this.trigger.alias,
confirmText: this.hass.localize("ui.common.submit"),
});