mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-14 04:46:34 +00:00
Show triggered in automation editor (#11771)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
1e6f402d0f
commit
f5b5414461
@ -11,7 +11,7 @@ export const debounce = <T extends any[]>(
|
|||||||
immediate = false
|
immediate = false
|
||||||
) => {
|
) => {
|
||||||
let timeout: number | undefined;
|
let timeout: number | undefined;
|
||||||
return (...args: T): void => {
|
const debouncedFunc = (...args: T): void => {
|
||||||
const later = () => {
|
const later = () => {
|
||||||
timeout = undefined;
|
timeout = undefined;
|
||||||
if (!immediate) {
|
if (!immediate) {
|
||||||
@ -25,4 +25,8 @@ export const debounce = <T extends any[]>(
|
|||||||
func(...args);
|
func(...args);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
debouncedFunc.cancel = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
};
|
||||||
|
return debouncedFunc;
|
||||||
};
|
};
|
||||||
|
19
src/data/config.ts
Normal file
19
src/data/config.ts
Normal 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,
|
||||||
|
});
|
@ -1,11 +1,13 @@
|
|||||||
|
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiDotsVertical } from "@mdi/js";
|
import { mdiDotsVertical } from "@mdi/js";
|
||||||
import "@material/mwc-select";
|
import "@material/mwc-select";
|
||||||
import type { Select } from "@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 { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
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";
|
||||||
@ -16,7 +18,7 @@ import "../../../../components/ha-card";
|
|||||||
import "../../../../components/ha-alert";
|
import "../../../../components/ha-alert";
|
||||||
import "../../../../components/ha-textfield";
|
import "../../../../components/ha-textfield";
|
||||||
import "../../../../components/ha-icon-button";
|
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 { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
import { haStyle } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
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-time_pattern";
|
||||||
import "./types/ha-automation-trigger-webhook";
|
import "./types/ha-automation-trigger-webhook";
|
||||||
import "./types/ha-automation-trigger-zone";
|
import "./types/ha-automation-trigger-zone";
|
||||||
|
import { debounce } from "../../../../common/util/debounce";
|
||||||
|
import { validateConfig } from "../../../../data/config";
|
||||||
|
|
||||||
const OPTIONS = [
|
const OPTIONS = [
|
||||||
"device",
|
"device",
|
||||||
@ -90,6 +94,12 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
|
|
||||||
@state() private _requestShowId = false;
|
@state() private _requestShowId = false;
|
||||||
|
|
||||||
|
@state() private _triggered = false;
|
||||||
|
|
||||||
|
@state() private _triggerColor = false;
|
||||||
|
|
||||||
|
private _triggerUnsub?: Promise<UnsubscribeFunc>;
|
||||||
|
|
||||||
private _processedTypes = memoizeOne(
|
private _processedTypes = memoizeOne(
|
||||||
(localize: LocalizeFunc): [string, string][] =>
|
(localize: LocalizeFunc): [string, string][] =>
|
||||||
OPTIONS.map(
|
OPTIONS.map(
|
||||||
@ -219,10 +229,98 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
</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>
|
</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) {
|
private _handleUiModeNotAvailable(ev: CustomEvent) {
|
||||||
this._warnings = handleStructError(this.hass, ev.detail).warnings;
|
this._warnings = handleStructError(this.hass, ev.detail).warnings;
|
||||||
if (!this._yamlMode) {
|
if (!this._yamlMode) {
|
||||||
@ -327,6 +425,31 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
z-index: 3;
|
z-index: 3;
|
||||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
--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 {
|
.rtl .card-menu {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
@ -1623,6 +1623,7 @@
|
|||||||
"header": "Triggers",
|
"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.",
|
"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",
|
"learn_more": "Learn more about triggers",
|
||||||
|
"triggered": "Triggered",
|
||||||
"add": "Add trigger",
|
"add": "Add trigger",
|
||||||
"id": "Trigger ID",
|
"id": "Trigger ID",
|
||||||
"edit_id": "Edit trigger ID",
|
"edit_id": "Edit trigger ID",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user