Show triggered in automation editor (#11771)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Paulus Schoutsen 2022-02-22 14:03:32 -08:00 committed by GitHub
parent 1e6f402d0f
commit f5b5414461
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 150 additions and 3 deletions

View File

@ -11,7 +11,7 @@ export const debounce = <T extends any[]>(
immediate = false
) => {
let timeout: number | undefined;
return (...args: T): void => {
const debouncedFunc = (...args: T): void => {
const later = () => {
timeout = undefined;
if (!immediate) {
@ -25,4 +25,8 @@ export const debounce = <T extends any[]>(
func(...args);
}
};
debouncedFunc.cancel = () => {
clearTimeout(timeout);
};
return debouncedFunc;
};

19
src/data/config.ts Normal file
View File

@ -0,0 +1,19 @@
import { HomeAssistant } from "../types";
interface ValidationResult {
valid: boolean;
error: string | null;
}
type ValidKeys = "trigger" | "action" | "condition";
export const validateConfig = <
T extends Partial<{ [key in ValidKeys]: unknown }>
>(
hass: HomeAssistant,
config: T
): Promise<Record<keyof T, ValidationResult>> =>
hass.callWS({
type: "validate_config",
...config,
});

View File

@ -1,11 +1,13 @@
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import "@material/mwc-select";
import type { Select } from "@material/mwc-select";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { classMap } from "lit/directives/class-map";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stringCompare } from "../../../../common/string/compare";
@ -16,7 +18,7 @@ import "../../../../components/ha-card";
import "../../../../components/ha-alert";
import "../../../../components/ha-textfield";
import "../../../../components/ha-icon-button";
import type { Trigger } from "../../../../data/automation";
import { subscribeTrigger, Trigger } from "../../../../data/automation";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
@ -34,6 +36,8 @@ 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 { debounce } from "../../../../common/util/debounce";
import { validateConfig } from "../../../../data/config";
const OPTIONS = [
"device",
@ -90,6 +94,12 @@ export default class HaAutomationTriggerRow extends LitElement {
@state() private _requestShowId = false;
@state() private _triggered = false;
@state() private _triggerColor = false;
private _triggerUnsub?: Promise<UnsubscribeFunc>;
private _processedTypes = memoizeOne(
(localize: LocalizeFunc): [string, string][] =>
OPTIONS.map(
@ -219,10 +229,98 @@ export default class HaAutomationTriggerRow extends LitElement {
</div>
`}
</div>
<div
class="triggered ${classMap({
active: this._triggered,
accent: this._triggerColor,
})}"
>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.triggered"
)}
</div>
</ha-card>
`;
}
protected override updated(changedProps: PropertyValues): void {
super.updated(changedProps);
if (changedProps.has("trigger")) {
this._subscribeTrigger();
}
}
public connectedCallback(): void {
super.connectedCallback();
if (this.hasUpdated && this.trigger) {
this._subscribeTrigger();
}
}
public disconnectedCallback(): void {
super.disconnectedCallback();
if (this._triggerUnsub) {
this._triggerUnsub.then((unsub) => unsub());
this._triggerUnsub = undefined;
}
this._doSubscribeTrigger.cancel();
}
private _subscribeTrigger() {
// Clean up old trigger subscription.
if (this._triggerUnsub) {
this._triggerUnsub.then((unsub) => unsub());
this._triggerUnsub = undefined;
}
this._doSubscribeTrigger();
}
private _doSubscribeTrigger = debounce(async () => {
let untriggerTimeout: number | undefined;
const showTriggeredTime = 5000;
const trigger = this.trigger;
// Clean up old trigger subscription.
if (this._triggerUnsub) {
this._triggerUnsub.then((unsub) => unsub());
this._triggerUnsub = undefined;
}
const validateResult = await validateConfig(this.hass, {
trigger: this.trigger,
});
// Don't do anything if trigger not valid or if trigger changed.
if (!validateResult.trigger.valid || this.trigger !== trigger) {
return;
}
const triggerUnsub = subscribeTrigger(
this.hass,
() => {
if (untriggerTimeout !== undefined) {
clearTimeout(untriggerTimeout);
this._triggerColor = !this._triggerColor;
} else {
this._triggerColor = false;
}
this._triggered = true;
untriggerTimeout = window.setTimeout(() => {
this._triggered = false;
untriggerTimeout = undefined;
}, showTriggeredTime);
},
trigger
);
triggerUnsub.catch(() => {
if (this._triggerUnsub === triggerUnsub) {
this._triggerUnsub = undefined;
}
});
this._triggerUnsub = triggerUnsub;
}, 5000);
private _handleUiModeNotAvailable(ev: CustomEvent) {
this._warnings = handleStructError(this.hass, ev.detail).warnings;
if (!this._yamlMode) {
@ -327,6 +425,31 @@ export default class HaAutomationTriggerRow extends LitElement {
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
.triggered {
position: absolute;
top: 0px;
right: 0px;
left: 0px;
text-transform: uppercase;
pointer-events: none;
font-weight: bold;
font-size: 14px;
background-color: var(--primary-color);
color: var(--text-primary-color);
max-height: 0px;
overflow: hidden;
transition: max-height 0.3s;
text-align: center;
border-top-right-radius: var(--ha-card-border-radius, 4px);
border-top-left-radius: var(--ha-card-border-radius, 4px);
}
.triggered.active {
max-height: 100px;
}
.triggered.accent {
background-color: var(--accent-color);
color: var(--text-accent-color, var(--text-primary-color));
}
.rtl .card-menu {
float: left;
}

View File

@ -1623,6 +1623,7 @@
"header": "Triggers",
"introduction": "Triggers are what starts the processing of an automation rule. It is possible to specify multiple triggers for the same rule. Once a trigger starts, Home Assistant will validate the conditions, if any, and call the action.",
"learn_more": "Learn more about triggers",
"triggered": "Triggered",
"add": "Add trigger",
"id": "Trigger ID",
"edit_id": "Edit trigger ID",