mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-26 02:36:37 +00:00
Add sensor offset to time trigger UI (#21957)
* Add sensor offset to time trigger UI * refactor long expression * memoize data * fix for trigger platform migration
This commit is contained in:
parent
82b50a1c5d
commit
79c71cbe48
@ -167,7 +167,7 @@ export interface TagTrigger extends BaseTrigger {
|
|||||||
|
|
||||||
export interface TimeTrigger extends BaseTrigger {
|
export interface TimeTrigger extends BaseTrigger {
|
||||||
trigger: "time";
|
trigger: "time";
|
||||||
at: string;
|
at: string | { entity_id: string; offset?: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TemplateTrigger extends BaseTrigger {
|
export interface TemplateTrigger extends BaseTrigger {
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
||||||
import { computeAttributeNameDisplay } from "../common/entity/compute_attribute_display";
|
import { computeAttributeNameDisplay } from "../common/entity/compute_attribute_display";
|
||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
|
import { isValidEntityId } from "../common/entity/valid_entity_id";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import { Condition, ForDict, Trigger } from "./automation";
|
import { Condition, ForDict, Trigger } from "./automation";
|
||||||
import {
|
import {
|
||||||
@ -371,13 +372,22 @@ const tryDescribeTrigger = (
|
|||||||
|
|
||||||
// Time Trigger
|
// Time Trigger
|
||||||
if (trigger.trigger === "time" && trigger.at) {
|
if (trigger.trigger === "time" && trigger.at) {
|
||||||
const result = ensureArray(trigger.at).map((at) =>
|
const result = ensureArray(trigger.at).map((at) => {
|
||||||
typeof at !== "string"
|
if (typeof at === "string") {
|
||||||
? at
|
if (isValidEntityId(at)) {
|
||||||
: at.includes(".")
|
return `entity ${hass.states[at] ? computeStateName(hass.states[at]) : at}`;
|
||||||
? `entity ${hass.states[at] ? computeStateName(hass.states[at]) : at}`
|
}
|
||||||
: localizeTimeString(at, hass.locale, hass.config)
|
return localizeTimeString(at, hass.locale, hass.config);
|
||||||
);
|
}
|
||||||
|
const entityStr = `entity ${hass.states[at.entity_id] ? computeStateName(hass.states[at.entity_id]) : at.entity_id}`;
|
||||||
|
const offsetStr = at.offset
|
||||||
|
? " " +
|
||||||
|
hass.localize(`${triggerTranslationBaseKey}.time.offset_by`, {
|
||||||
|
offset: describeDuration(hass.locale, at.offset),
|
||||||
|
})
|
||||||
|
: "";
|
||||||
|
return `${entityStr}${offsetStr}`;
|
||||||
|
});
|
||||||
|
|
||||||
return hass.localize(`${triggerTranslationBaseKey}.time.description.full`, {
|
return hass.localize(`${triggerTranslationBaseKey}.time.description.full`, {
|
||||||
time: formatListWithOrs(hass.locale, result),
|
time: formatListWithOrs(hass.locale, result),
|
||||||
|
@ -9,6 +9,9 @@ import type { TimeTrigger } from "../../../../../data/automation";
|
|||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import type { TriggerElement } from "../ha-automation-trigger-row";
|
import type { TriggerElement } from "../ha-automation-trigger-row";
|
||||||
|
|
||||||
|
const MODE_TIME = "time";
|
||||||
|
const MODE_ENTITY = "entity";
|
||||||
|
|
||||||
@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;
|
||||||
@ -17,48 +20,60 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@state() private _inputMode?: boolean;
|
@state() private _inputMode:
|
||||||
|
| undefined
|
||||||
|
| typeof MODE_TIME
|
||||||
|
| typeof MODE_ENTITY;
|
||||||
|
|
||||||
public static get defaultConfig(): TimeTrigger {
|
public static get defaultConfig(): TimeTrigger {
|
||||||
return { trigger: "time", at: "" };
|
return { trigger: "time", at: "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
private _schema = memoizeOne(
|
private _schema = memoizeOne(
|
||||||
(localize: LocalizeFunc, inputMode?: boolean) => {
|
(
|
||||||
const atSelector = inputMode
|
localize: LocalizeFunc,
|
||||||
? {
|
inputMode: typeof MODE_TIME | typeof MODE_ENTITY,
|
||||||
entity: {
|
showOffset: boolean
|
||||||
filter: [
|
) =>
|
||||||
{ domain: "input_datetime" },
|
[
|
||||||
{ domain: "sensor", device_class: "timestamp" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: { time: {} };
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
name: "mode",
|
name: "mode",
|
||||||
type: "select",
|
type: "select",
|
||||||
required: true,
|
required: true,
|
||||||
options: [
|
options: [
|
||||||
[
|
[
|
||||||
"value",
|
MODE_TIME,
|
||||||
localize(
|
localize(
|
||||||
"ui.panel.config.automation.editor.triggers.type.time.type_value"
|
"ui.panel.config.automation.editor.triggers.type.time.type_value"
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"input",
|
MODE_ENTITY,
|
||||||
localize(
|
localize(
|
||||||
"ui.panel.config.automation.editor.triggers.type.time.type_input"
|
"ui.panel.config.automation.editor.triggers.type.time.type_input"
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ name: "at", selector: atSelector },
|
...(inputMode === MODE_TIME
|
||||||
] as const;
|
? ([{ name: "time", selector: { time: {} } }] as const)
|
||||||
}
|
: ([
|
||||||
|
{
|
||||||
|
name: "entity",
|
||||||
|
selector: {
|
||||||
|
entity: {
|
||||||
|
filter: [
|
||||||
|
{ domain: "input_datetime" },
|
||||||
|
{ domain: "sensor", device_class: "timestamp" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as const)),
|
||||||
|
...(showOffset
|
||||||
|
? ([{ name: "offset", selector: { text: {} } }] as const)
|
||||||
|
: ([] as const)),
|
||||||
|
] as const
|
||||||
);
|
);
|
||||||
|
|
||||||
public willUpdate(changedProperties: PropertyValues) {
|
public willUpdate(changedProperties: PropertyValues) {
|
||||||
@ -75,23 +90,46 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _data = memoizeOne(
|
||||||
|
(
|
||||||
|
inputMode: undefined | typeof MODE_ENTITY | typeof MODE_TIME,
|
||||||
|
at:
|
||||||
|
| string
|
||||||
|
| { entity_id: string | undefined; offset?: string | undefined }
|
||||||
|
): {
|
||||||
|
mode: typeof MODE_TIME | typeof MODE_ENTITY;
|
||||||
|
entity: string | undefined;
|
||||||
|
time: string | undefined;
|
||||||
|
offset: string | undefined;
|
||||||
|
} => {
|
||||||
|
const entity =
|
||||||
|
typeof at === "object"
|
||||||
|
? at.entity_id
|
||||||
|
: at?.startsWith("input_datetime.") || at?.startsWith("sensor.")
|
||||||
|
? at
|
||||||
|
: undefined;
|
||||||
|
const time = entity ? undefined : (at as string | undefined);
|
||||||
|
const offset = typeof at === "object" ? at.offset : undefined;
|
||||||
|
const mode = inputMode ?? (entity ? MODE_ENTITY : MODE_TIME);
|
||||||
|
return {
|
||||||
|
mode,
|
||||||
|
entity,
|
||||||
|
time,
|
||||||
|
offset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const at = this.trigger.at;
|
const at = this.trigger.at;
|
||||||
|
|
||||||
if (Array.isArray(at)) {
|
if (Array.isArray(at)) {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
const data = this._data(this._inputMode, at);
|
||||||
const inputMode =
|
const showOffset =
|
||||||
this._inputMode ??
|
data.mode === MODE_ENTITY && data.entity?.startsWith("sensor.");
|
||||||
(at?.startsWith("input_datetime.") || at?.startsWith("sensor."));
|
const schema = this._schema(this.hass.localize, data.mode, !!showOffset);
|
||||||
|
|
||||||
const schema = this._schema(this.hass.localize, inputMode);
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
mode: inputMode ? "input" : "value",
|
|
||||||
...this.trigger,
|
|
||||||
};
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-form
|
<ha-form
|
||||||
@ -107,26 +145,43 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
|||||||
|
|
||||||
private _valueChanged(ev: CustomEvent): void {
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const newValue = ev.detail.value;
|
const newValue = { ...ev.detail.value };
|
||||||
|
this._inputMode = newValue.mode;
|
||||||
this._inputMode = newValue.mode === "input";
|
if (newValue.mode === MODE_TIME) {
|
||||||
delete newValue.mode;
|
delete newValue.entity;
|
||||||
|
delete newValue.offset;
|
||||||
Object.keys(newValue).forEach((key) =>
|
} else {
|
||||||
newValue[key] === undefined || newValue[key] === ""
|
delete newValue.time;
|
||||||
? delete newValue[key]
|
if (!newValue.entity?.startsWith("sensor.")) {
|
||||||
: {}
|
delete newValue.offset;
|
||||||
);
|
}
|
||||||
|
}
|
||||||
fireEvent(this, "value-changed", { value: newValue });
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.trigger,
|
||||||
|
at: newValue.offset
|
||||||
|
? {
|
||||||
|
entity_id: newValue.entity,
|
||||||
|
offset: newValue.offset,
|
||||||
|
}
|
||||||
|
: newValue.entity || newValue.time,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeLabelCallback = (
|
private _computeLabelCallback = (
|
||||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||||
): string =>
|
): string => {
|
||||||
this.hass.localize(
|
switch (schema.name) {
|
||||||
|
case "time":
|
||||||
|
return this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.triggers.type.time.at`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.triggers.type.time.${schema.name}`
|
`ui.panel.config.automation.editor.triggers.type.time.${schema.name}`
|
||||||
);
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -3054,6 +3054,9 @@
|
|||||||
"type_input": "Value of a date/time helper or timestamp-class sensor",
|
"type_input": "Value of a date/time helper or timestamp-class sensor",
|
||||||
"label": "Time",
|
"label": "Time",
|
||||||
"at": "At time",
|
"at": "At time",
|
||||||
|
"offset": "[%key:ui::panel::config::automation::editor::triggers::type::sun::offset%]",
|
||||||
|
"entity": "Entity with timestamp",
|
||||||
|
"offset_by": "offset by {offset}",
|
||||||
"mode": "Mode",
|
"mode": "Mode",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "At a specific time, or on a specific date.",
|
"picker": "At a specific time, or on a specific date.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user