diff --git a/src/data/automation.ts b/src/data/automation.ts
index 49d23f069f08..a85089bff0ff 100644
--- a/src/data/automation.ts
+++ b/src/data/automation.ts
@@ -256,6 +256,13 @@ export interface ZoneCondition extends BaseCondition {
type Weekday = "sun" | "mon" | "tue" | "wed" | "thu" | "fri" | "sat";
+export interface DateCondition extends BaseCondition {
+ condition: "date";
+ after?: string;
+ before?: string;
+ weekday?: Weekday | Weekday[];
+}
+
export interface TimeCondition extends BaseCondition {
condition: "time";
after?: string;
@@ -263,6 +270,13 @@ export interface TimeCondition extends BaseCondition {
weekday?: Weekday | Weekday[];
}
+export interface DatetimeCondition extends BaseCondition {
+ condition: "datetime";
+ after?: string;
+ before?: string;
+ weekday?: Weekday | Weekday[];
+}
+
export interface TemplateCondition extends BaseCondition {
condition: "template";
value_template: string;
@@ -300,7 +314,9 @@ export type Condition =
| NumericStateCondition
| SunCondition
| ZoneCondition
+ | DateCondition
| TimeCondition
+ | DatetimeCondition
| TemplateCondition
| DeviceCondition
| LogicalCondition
diff --git a/src/panels/config/automation/condition/ha-automation-condition-editor.ts b/src/panels/config/automation/condition/ha-automation-condition-editor.ts
index a4734a1e8249..81346e699a02 100644
--- a/src/panels/config/automation/condition/ha-automation-condition-editor.ts
+++ b/src/panels/config/automation/condition/ha-automation-condition-editor.ts
@@ -9,6 +9,8 @@ import { expandConditionWithShorthand } from "../../../../data/automation";
import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import "./types/ha-automation-condition-and";
+import "./types/ha-automation-condition-date";
+import "./types/ha-automation-condition-datetime";
import "./types/ha-automation-condition-device";
import "./types/ha-automation-condition-not";
import "./types/ha-automation-condition-numeric_state";
diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-date.ts b/src/panels/config/automation/condition/types/ha-automation-condition-date.ts
new file mode 100644
index 000000000000..95e9166dc355
--- /dev/null
+++ b/src/panels/config/automation/condition/types/ha-automation-condition-date.ts
@@ -0,0 +1,193 @@
+import { html, LitElement } from "lit";
+import { customElement, property, state } from "lit/decorators";
+import memoizeOne from "memoize-one";
+import { firstWeekdayIndex } from "../../../../../common/datetime/first_weekday";
+import { fireEvent } from "../../../../../common/dom/fire_event";
+import type { LocalizeFunc } from "../../../../../common/translations/localize";
+import "../../../../../components/ha-form/ha-form";
+import type { SchemaUnion } from "../../../../../components/ha-form/types";
+import type { DateCondition } from "../../../../../data/automation";
+import type { FrontendLocaleData } from "../../../../../data/translation";
+import type { HomeAssistant } from "../../../../../types";
+import type { ConditionElement } from "../ha-automation-condition-row";
+
+const DAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"] as const;
+
+@customElement("ha-automation-condition-date")
+export class HaDateCondition extends LitElement implements ConditionElement {
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property({ attribute: false }) public condition!: DateCondition;
+
+ @state() private _inputModeBefore?: boolean;
+
+ @state() private _inputModeAfter?: boolean;
+
+ @property({ type: Boolean }) public disabled = false;
+
+ public static get defaultConfig(): DateCondition {
+ return { condition: "date" };
+ }
+
+ private _schema = memoizeOne(
+ (
+ localize: LocalizeFunc,
+ locale: FrontendLocaleData,
+ inputModeAfter?: boolean,
+ inputModeBefore?: boolean
+ ) => {
+ const dayIndex = firstWeekdayIndex(locale);
+ const sortedDays = DAYS.slice(dayIndex, DAYS.length).concat(
+ DAYS.slice(0, dayIndex)
+ );
+ return [
+ {
+ name: "mode_after",
+ type: "select",
+ required: true,
+ options: [
+ [
+ "value",
+ localize(
+ "ui.panel.config.automation.editor.conditions.type.date.type_value"
+ ),
+ ],
+ [
+ "input",
+ localize(
+ "ui.panel.config.automation.editor.conditions.type.date.type_input"
+ ),
+ ],
+ ],
+ },
+ {
+ name: "after",
+ selector: inputModeAfter
+ ? {
+ entity: {
+ filter: [
+ { domain: "input_datetime" },
+ { domain: "sensor", device_class: "timestamp" },
+ ],
+ },
+ }
+ : { date: {} },
+ },
+ {
+ name: "mode_before",
+ type: "select",
+ required: true,
+ options: [
+ [
+ "value",
+ localize(
+ "ui.panel.config.automation.editor.conditions.type.date.type_value"
+ ),
+ ],
+ [
+ "input",
+ localize(
+ "ui.panel.config.automation.editor.conditions.type.date.type_input"
+ ),
+ ],
+ ],
+ },
+ {
+ name: "before",
+ selector: inputModeBefore
+ ? {
+ entity: {
+ filter: [
+ { domain: "input_datetime" },
+ { domain: "sensor", device_class: "timestamp" },
+ ],
+ },
+ }
+ : { date: {} },
+ },
+ {
+ type: "multi_select",
+ name: "weekday",
+ options: sortedDays.map(
+ (day) =>
+ [
+ day,
+ localize(
+ `ui.panel.config.automation.editor.conditions.type.date.weekdays.${day}`
+ ),
+ ] as const
+ ),
+ },
+ ] as const;
+ }
+ );
+
+ protected render() {
+ const inputModeBefore =
+ this._inputModeBefore ??
+ (this.condition.before?.startsWith("input_datetime.") ||
+ this.condition.before?.startsWith("sensor."));
+ const inputModeAfter =
+ this._inputModeAfter ??
+ (this.condition.after?.startsWith("input_datetime.") ||
+ this.condition.after?.startsWith("sensor."));
+
+ const schema = this._schema(
+ this.hass.localize,
+ this.hass.locale,
+ inputModeAfter,
+ inputModeBefore
+ );
+
+ const data = {
+ mode_before: inputModeBefore ? "input" : "value",
+ mode_after: inputModeAfter ? "input" : "value",
+ ...this.condition,
+ };
+
+ return html`
+
+ `;
+ }
+
+ private _valueChanged(ev: CustomEvent): void {
+ ev.stopPropagation();
+ const newValue = ev.detail.value;
+
+ this._inputModeAfter = newValue.mode_after === "input";
+ this._inputModeBefore = newValue.mode_before === "input";
+
+ delete newValue.mode_after;
+ delete newValue.mode_before;
+
+ Object.keys(newValue).forEach((key) =>
+ newValue[key] === undefined ||
+ newValue[key] === "" ||
+ (Array.isArray(newValue[key]) && newValue[key].length === 0)
+ ? delete newValue[key]
+ : {}
+ );
+
+ fireEvent(this, "value-changed", { value: newValue });
+ }
+
+ private _computeLabelCallback = (
+ schema: SchemaUnion>
+ ): string =>
+ this.hass.localize(
+ `ui.panel.config.automation.editor.conditions.type.date.${schema.name}`
+ );
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-automation-condition-date": HaDateCondition;
+ }
+}
diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-datetime.ts b/src/panels/config/automation/condition/types/ha-automation-condition-datetime.ts
new file mode 100644
index 000000000000..efe417255685
--- /dev/null
+++ b/src/panels/config/automation/condition/types/ha-automation-condition-datetime.ts
@@ -0,0 +1,196 @@
+import { html, LitElement } from "lit";
+import { customElement, property, state } from "lit/decorators";
+import memoizeOne from "memoize-one";
+import { firstWeekdayIndex } from "../../../../../common/datetime/first_weekday";
+import { fireEvent } from "../../../../../common/dom/fire_event";
+import type { LocalizeFunc } from "../../../../../common/translations/localize";
+import "../../../../../components/ha-form/ha-form";
+import type { SchemaUnion } from "../../../../../components/ha-form/types";
+import type { DatetimeCondition } from "../../../../../data/automation";
+import type { FrontendLocaleData } from "../../../../../data/translation";
+import type { HomeAssistant } from "../../../../../types";
+import type { ConditionElement } from "../ha-automation-condition-row";
+
+const DAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"] as const;
+
+@customElement("ha-automation-condition-datetime")
+export class HaDatetimeCondition
+ extends LitElement
+ implements ConditionElement
+{
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property({ attribute: false }) public condition!: DatetimeCondition;
+
+ @state() private _inputModeBefore?: boolean;
+
+ @state() private _inputModeAfter?: boolean;
+
+ @property({ type: Boolean }) public disabled = false;
+
+ public static get defaultConfig(): DatetimeCondition {
+ return { condition: "datetime" };
+ }
+
+ private _schema = memoizeOne(
+ (
+ localize: LocalizeFunc,
+ locale: FrontendLocaleData,
+ inputModeAfter?: boolean,
+ inputModeBefore?: boolean
+ ) => {
+ const dayIndex = firstWeekdayIndex(locale);
+ const sortedDays = DAYS.slice(dayIndex, DAYS.length).concat(
+ DAYS.slice(0, dayIndex)
+ );
+ return [
+ {
+ name: "mode_after",
+ type: "select",
+ required: true,
+ options: [
+ [
+ "value",
+ localize(
+ "ui.panel.config.automation.editor.conditions.type.datetime.type_value"
+ ),
+ ],
+ [
+ "input",
+ localize(
+ "ui.panel.config.automation.editor.conditions.type.datetime.type_input"
+ ),
+ ],
+ ],
+ },
+ {
+ name: "after",
+ selector: inputModeAfter
+ ? {
+ entity: {
+ filter: [
+ { domain: "input_datetime" },
+ { domain: "sensor", device_class: "timestamp" },
+ ],
+ },
+ }
+ : { datetime: {} },
+ },
+ {
+ name: "mode_before",
+ type: "select",
+ required: true,
+ options: [
+ [
+ "value",
+ localize(
+ "ui.panel.config.automation.editor.conditions.type.datetime.type_value"
+ ),
+ ],
+ [
+ "input",
+ localize(
+ "ui.panel.config.automation.editor.conditions.type.datetime.type_input"
+ ),
+ ],
+ ],
+ },
+ {
+ name: "before",
+ selector: inputModeBefore
+ ? {
+ entity: {
+ filter: [
+ { domain: "input_datetime" },
+ { domain: "sensor", device_class: "timestamp" },
+ ],
+ },
+ }
+ : { datetime: {} },
+ },
+ {
+ type: "multi_select",
+ name: "weekday",
+ options: sortedDays.map(
+ (day) =>
+ [
+ day,
+ localize(
+ `ui.panel.config.automation.editor.conditions.type.datetime.weekdays.${day}`
+ ),
+ ] as const
+ ),
+ },
+ ] as const;
+ }
+ );
+
+ protected render() {
+ const inputModeBefore =
+ this._inputModeBefore ??
+ (this.condition.before?.startsWith("input_datetime.") ||
+ this.condition.before?.startsWith("sensor."));
+ const inputModeAfter =
+ this._inputModeAfter ??
+ (this.condition.after?.startsWith("input_datetime.") ||
+ this.condition.after?.startsWith("sensor."));
+
+ const schema = this._schema(
+ this.hass.localize,
+ this.hass.locale,
+ inputModeAfter,
+ inputModeBefore
+ );
+
+ const data = {
+ mode_before: inputModeBefore ? "input" : "value",
+ mode_after: inputModeAfter ? "input" : "value",
+ ...this.condition,
+ };
+
+ return html`
+
+ `;
+ }
+
+ private _valueChanged(ev: CustomEvent): void {
+ ev.stopPropagation();
+ const newValue = ev.detail.value;
+
+ this._inputModeAfter = newValue.mode_after === "input";
+ this._inputModeBefore = newValue.mode_before === "input";
+
+ delete newValue.mode_after;
+ delete newValue.mode_before;
+
+ Object.keys(newValue).forEach((key) =>
+ newValue[key] === undefined ||
+ newValue[key] === "" ||
+ (Array.isArray(newValue[key]) && newValue[key].length === 0)
+ ? delete newValue[key]
+ : {}
+ );
+
+ fireEvent(this, "value-changed", { value: newValue });
+ }
+
+ private _computeLabelCallback = (
+ schema: SchemaUnion>
+ ): string =>
+ this.hass.localize(
+ `ui.panel.config.automation.editor.conditions.type.datetime.${schema.name}`
+ );
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-automation-condition-datetime": HaDatetimeCondition;
+ }
+}
diff --git a/src/translations/en.json b/src/translations/en.json
index c658e7933818..e5e503ee03de 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -3327,6 +3327,29 @@
"full": "If template renders a value equal to true"
}
},
+ "date": {
+ "type_value": "[%key:ui::panel::config::automation::editor::triggers::type::time::type_value%]",
+ "type_input": "[%key:ui::panel::config::automation::editor::triggers::type::time::type_input%]",
+ "label": "Date",
+ "after": "After",
+ "before": "Before",
+ "weekday": "Weekdays",
+ "mode_after": "[%key:ui::panel::config::automation::editor::conditions::type::date::after%]",
+ "mode_before": "[%key:ui::panel::config::automation::editor::conditions::type::date::before%]",
+ "weekdays": {
+ "mon": "[%key:ui::weekdays::monday%]",
+ "tue": "[%key:ui::weekdays::tuesday%]",
+ "wed": "[%key:ui::weekdays::wednesday%]",
+ "thu": "[%key:ui::weekdays::thursday%]",
+ "fri": "[%key:ui::weekdays::friday%]",
+ "sat": "[%key:ui::weekdays::saturday%]",
+ "sun": "[%key:ui::weekdays::sunday%]"
+ },
+ "description": {
+ "picker": "If the current date is before or after a specified date.",
+ "full": "Confirm the {hasTime, select, \n after {date is after {time_after}}\n before {date is before {date_before}}\n after_before {date is after {time_after} and before {date_before}} \n other {}\n }{hasTimeAndDay, select, \n true { and the }\n other {}\n}{hasDay, select, \n true { day is {day}}\n other {}\n}"
+ }
+ },
"time": {
"type_value": "[%key:ui::panel::config::automation::editor::triggers::type::time::type_value%]",
"type_input": "[%key:ui::panel::config::automation::editor::triggers::type::time::type_input%]",
@@ -3337,19 +3360,42 @@
"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": {
- "mon": "Monday",
- "tue": "Tuesday",
- "wed": "Wednesday",
- "thu": "Thursday",
- "fri": "Friday",
- "sat": "Saturday",
- "sun": "Sunday"
+ "mon": "[%key:ui::weekdays::monday%]",
+ "tue": "[%key:ui::weekdays::tuesday%]",
+ "wed": "[%key:ui::weekdays::wednesday%]",
+ "thu": "[%key:ui::weekdays::thursday%]",
+ "fri": "[%key:ui::weekdays::friday%]",
+ "sat": "[%key:ui::weekdays::saturday%]",
+ "sun": "[%key:ui::weekdays::sunday%]"
},
"description": {
"picker": "If the current time is before or after a specified time.",
"full": "If the {hasTime, select, \n after {time is after {time_after}}\n before {time is before {time_before}}\n after_before {time is after {time_after} and before {time_before}} \n other {}\n }{hasTimeAndDay, select, \n true { and the }\n other {}\n}{hasDay, select, \n true { day is {day}}\n other {}\n}"
}
},
+ "datetime": {
+ "type_value": "[%key:ui::panel::config::automation::editor::triggers::type::time::type_value%]",
+ "type_input": "[%key:ui::panel::config::automation::editor::triggers::type::time::type_input%]",
+ "label": "Date and time",
+ "after": "After",
+ "before": "Before",
+ "weekday": "Weekdays",
+ "mode_after": "[%key:ui::panel::config::automation::editor::conditions::type::datetime::after%]",
+ "mode_before": "[%key:ui::panel::config::automation::editor::conditions::type::datetime::before%]",
+ "weekdays": {
+ "mon": "[%key:ui::weekdays::monday%]",
+ "tue": "[%key:ui::weekdays::tuesday%]",
+ "wed": "[%key:ui::weekdays::wednesday%]",
+ "thu": "[%key:ui::weekdays::thursday%]",
+ "fri": "[%key:ui::weekdays::friday%]",
+ "sat": "[%key:ui::weekdays::saturday%]",
+ "sun": "[%key:ui::weekdays::sunday%]"
+ },
+ "description": {
+ "picker": "If the current datetime is before or after a specified datetime.",
+ "full": "Confirm the {hasTime, select, \n after {time is after {time_after}}\n before {time is before {time_before}}\n after_before {time is after {time_after} and before {time_before}} \n other {}\n }{hasTimeAndDay, select, \n true { and the }\n other {}\n}{hasDay, select, \n true { day is {day}}\n other {}\n}"
+ }
+ },
"trigger": {
"label": "Triggered by",
"no_triggers": "No triggers have an ID set. Use the three-dot menu on a trigger and select Edit ID to assign one.",