1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-02 08:33:31 +01:00
Files
frontend/src/data/automation_i18n.ts
2026-03-27 14:54:38 +02:00

1331 lines
37 KiB
TypeScript

import type { HassConfig, HassEntity } from "home-assistant-js-websocket";
import { ensureArray } from "../common/array/ensure-array";
import {
formatDurationLong,
formatNumericDuration,
} from "../common/datetime/format_duration";
import {
formatTime,
formatTimeWithSeconds,
} from "../common/datetime/format_time";
import secondsToDuration from "../common/datetime/seconds_to_duration";
import { computeAttributeNameDisplay } from "../common/entity/compute_attribute_display";
import { computeStateName } from "../common/entity/compute_state_name";
import { isValidEntityId } from "../common/entity/valid_entity_id";
import {
formatListWithAnds,
formatListWithOrs,
} from "../common/string/format-list";
import { hasTemplate } from "../common/string/has-template";
import type { HomeAssistant } from "../types";
import type {
Condition,
ForDict,
LegacyCondition,
LegacyTrigger,
Trigger,
} from "./automation";
import { getConditionDomain, getConditionObjectId } from "./condition";
import type {
DeviceCondition,
DeviceTrigger,
} from "./device/device_automation";
import {
localizeDeviceAutomationCondition,
localizeDeviceAutomationTrigger,
} from "./device/device_automation";
import type { EntityRegistryEntry } from "./entity/entity_registry";
import type { FrontendLocaleData } from "./translation";
import { getTriggerDomain, getTriggerObjectId, isTriggerList } from "./trigger";
const triggerTranslationBaseKey =
"ui.panel.config.automation.editor.triggers.type";
const conditionsTranslationBaseKey =
"ui.panel.config.automation.editor.conditions.type";
const describeDuration = (
locale: FrontendLocaleData,
forTime: number | string | ForDict
) => {
let duration: string | null;
if (typeof forTime === "number") {
duration = secondsToDuration(forTime);
} else if (typeof forTime === "string") {
duration = forTime;
} else {
duration = formatNumericDuration(locale, forTime);
}
return duration;
};
const localizeTimeString = (
time: string,
locale: FrontendLocaleData,
config: HassConfig
) => {
const chunks = time.split(":");
if (chunks.length < 2 || chunks.length > 3) {
return time;
}
try {
const dt = new Date("1970-01-01T" + time);
if (chunks.length === 2 || Number(chunks[2]) === 0) {
return formatTime(dt, locale, config);
}
return formatTimeWithSeconds(dt, locale, config);
} catch {
return time;
}
};
export const describeTrigger = (
trigger: Trigger,
hass: HomeAssistant,
entityRegistry: EntityRegistryEntry[],
ignoreAlias = false
): string => {
try {
const description = tryDescribeTrigger(
trigger,
hass,
entityRegistry,
ignoreAlias
);
if (typeof description !== "string") {
throw new Error(String(description));
}
return description;
} catch (error: any) {
// eslint-disable-next-line no-console
console.error(error);
let msg = "Error in describing trigger";
if (error.message) {
msg += ": " + error.message;
}
return msg;
}
};
const tryDescribeTrigger = (
trigger: Trigger,
hass: HomeAssistant,
entityRegistry: EntityRegistryEntry[],
ignoreAlias = false
) => {
if (isTriggerList(trigger)) {
const triggers = ensureArray(trigger.triggers);
if (!triggers || triggers.length === 0) {
return hass.localize(
`${triggerTranslationBaseKey}.list.description.no_trigger`
);
}
const count = triggers.length;
return hass.localize(`${triggerTranslationBaseKey}.list.description.full`, {
count: count,
});
}
if (trigger.alias && !ignoreAlias) {
return trigger.alias;
}
const description = describeLegacyTrigger(
trigger as LegacyTrigger,
hass,
entityRegistry
);
if (description) {
return description;
}
const triggerType = trigger.trigger;
const domain = getTriggerDomain(trigger.trigger);
const type = getTriggerObjectId(trigger.trigger);
return (
hass.localize(`component.${domain}.triggers.${type}.name`) ||
hass.localize(
`ui.panel.config.automation.editor.triggers.type.${triggerType as LegacyTrigger["trigger"]}.label`
) ||
hass.localize(`ui.panel.config.automation.editor.triggers.unknown_trigger`)
);
};
const describeLegacyTrigger = (
trigger: LegacyTrigger,
hass: HomeAssistant,
entityRegistry: EntityRegistryEntry[]
) => {
// Event Trigger
if (trigger.trigger === "event" && trigger.event_type) {
const eventTypes: string[] = [];
if (Array.isArray(trigger.event_type)) {
for (const state of trigger.event_type.values()) {
eventTypes.push(state);
}
} else {
eventTypes.push(trigger.event_type);
}
const eventTypesString = formatListWithOrs(hass.locale, eventTypes);
return hass.localize(
`${triggerTranslationBaseKey}.event.description.full`,
{ eventTypes: eventTypesString }
);
}
// Home Assistant Trigger
if (trigger.trigger === "homeassistant" && trigger.event) {
return hass.localize(
trigger.event === "start"
? `${triggerTranslationBaseKey}.homeassistant.description.started`
: `${triggerTranslationBaseKey}.homeassistant.description.shutdown`
);
}
// Numeric State Trigger
if (trigger.trigger === "numeric_state" && trigger.entity_id) {
const entities: string[] = [];
const states = hass.states;
const stateObj = Array.isArray(trigger.entity_id)
? hass.states[trigger.entity_id[0]]
: (hass.states[trigger.entity_id] as HassEntity | undefined);
if (Array.isArray(trigger.entity_id)) {
for (const entity of trigger.entity_id.values()) {
if (states[entity]) {
entities.push(computeStateName(states[entity]) || entity);
}
}
} else if (trigger.entity_id) {
entities.push(
states[trigger.entity_id]
? computeStateName(states[trigger.entity_id])
: trigger.entity_id
);
}
const attribute = trigger.attribute
? stateObj
? computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
trigger.attribute
)
: trigger.attribute
: undefined;
const duration = trigger.for
? describeDuration(hass.locale, trigger.for)
: undefined;
if (trigger.above !== undefined && trigger.below !== undefined) {
return hass.localize(
`${triggerTranslationBaseKey}.numeric_state.description.above-below`,
{
attribute: attribute,
entity: formatListWithOrs(hass.locale, entities),
numberOfEntities: entities.length,
above: trigger.above,
below: trigger.below,
duration: duration,
}
);
}
if (trigger.above !== undefined) {
return hass.localize(
`${triggerTranslationBaseKey}.numeric_state.description.above`,
{
attribute: attribute,
entity: formatListWithOrs(hass.locale, entities),
numberOfEntities: entities.length,
above: trigger.above,
duration: duration,
}
);
}
if (trigger.below !== undefined) {
return hass.localize(
`${triggerTranslationBaseKey}.numeric_state.description.below`,
{
attribute: attribute,
entity: formatListWithOrs(hass.locale, entities),
numberOfEntities: entities.length,
below: trigger.below,
duration: duration,
}
);
}
}
// State Trigger
if (trigger.trigger === "state") {
const entities: string[] = [];
const states = hass.states;
let attribute = "";
if (trigger.attribute) {
const stateObj = Array.isArray(trigger.entity_id)
? hass.states[trigger.entity_id[0]]
: (hass.states[trigger.entity_id] as HassEntity | undefined);
attribute = stateObj
? computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
trigger.attribute
)
: trigger.attribute;
}
const entityArray: string[] = ensureArray(trigger.entity_id);
if (entityArray) {
for (const entity of entityArray) {
if (states[entity]) {
entities.push(computeStateName(states[entity]) || entity);
}
}
}
const stateObj = hass.states[entityArray[0]] as HassEntity | undefined;
let fromChoice = "other";
let fromString = "";
if (trigger.from !== undefined) {
let fromArray: string[] = [];
if (trigger.from === null) {
if (!trigger.attribute) {
fromChoice = "null";
}
} else {
fromArray = ensureArray(trigger.from);
const from: string[] = [];
for (const state of fromArray) {
from.push(
stateObj
? trigger.attribute
? hass
.formatEntityAttributeValue(
stateObj,
trigger.attribute,
state
)
.toString()
: hass.formatEntityState(stateObj, state)
: state
);
}
if (from.length !== 0) {
fromString = formatListWithOrs(hass.locale, from);
fromChoice = "fromUsed";
}
}
}
let toChoice = "other";
let toString = "";
if (trigger.to !== undefined) {
let toArray: string[] = [];
if (trigger.to === null) {
if (!trigger.attribute) {
toChoice = "null";
}
} else {
toArray = ensureArray(trigger.to);
const to: string[] = [];
for (const state of toArray) {
to.push(
stateObj
? trigger.attribute
? hass
.formatEntityAttributeValue(
stateObj,
trigger.attribute,
state
)
.toString()
: hass.formatEntityState(stateObj, state).toString()
: state
);
}
if (to.length !== 0) {
toString = formatListWithOrs(hass.locale, to);
toChoice = "toUsed";
}
}
}
if (
!trigger.attribute &&
trigger.from === undefined &&
trigger.to === undefined
) {
toChoice = "special";
}
let duration = "";
if (trigger.for) {
duration = describeDuration(hass.locale, trigger.for) ?? "";
}
return hass.localize(
`${triggerTranslationBaseKey}.state.description.full`,
{
hasAttribute: attribute !== "" ? "true" : "false",
attribute: attribute,
hasEntity: entities.length !== 0 ? "true" : "false",
entity: formatListWithOrs(hass.locale, entities),
fromChoice: fromChoice,
fromString: fromString,
toChoice: toChoice,
toString: toString,
hasDuration: duration !== "" ? "true" : "false",
duration: duration,
}
);
}
// Sun Trigger
if (trigger.trigger === "sun" && trigger.event) {
let duration = "";
if (trigger.offset) {
if (typeof trigger.offset === "number") {
duration = secondsToDuration(trigger.offset)!;
} else if (typeof trigger.offset === "string") {
duration = trigger.offset;
} else {
duration = JSON.stringify(trigger.offset);
}
}
return hass.localize(
trigger.event === "sunset"
? `${triggerTranslationBaseKey}.sun.description.sets`
: `${triggerTranslationBaseKey}.sun.description.rises`,
{ hasDuration: duration !== "" ? "true" : "false", duration: duration }
);
}
// Tag Trigger
if (trigger.trigger === "tag") {
const entity = Object.values(hass.states).find(
(state) =>
state.entity_id.startsWith("tag.") &&
state.attributes.tag_id === trigger.tag_id
);
return entity
? hass.localize(
`${triggerTranslationBaseKey}.tag.description.known_tag`,
{ tag_name: computeStateName(entity) }
)
: hass.localize(`${triggerTranslationBaseKey}.tag.description.full`);
}
// Time Trigger
if (trigger.trigger === "time" && trigger.at) {
const result = ensureArray(trigger.at).map((at) => {
if (typeof at === "string") {
if (isValidEntityId(at)) {
return `entity ${hass.states[at] ? computeStateName(hass.states[at]) : at}`;
}
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}`;
});
// Handle weekday information if present
let weekdays: string[] = [];
if (trigger.weekday) {
const weekdayArray = ensureArray(trigger.weekday);
if (weekdayArray.length > 0) {
weekdays = weekdayArray.map((day) =>
hass.localize(
`ui.panel.config.automation.editor.triggers.type.time.weekdays.${day}` as any
)
);
}
}
return hass.localize(`${triggerTranslationBaseKey}.time.description.full`, {
time: formatListWithOrs(hass.locale, result),
hasWeekdays: weekdays.length > 0 ? "true" : "false",
weekdays: formatListWithOrs(hass.locale, weekdays),
});
}
// Time Pattern Trigger
if (trigger.trigger === "time_pattern") {
if (!trigger.seconds && !trigger.minutes && !trigger.hours) {
return hass.localize(
`${triggerTranslationBaseKey}.time_pattern.description.initial`
);
}
const invalidParts: ("seconds" | "minutes" | "hours")[] = [];
let secondsChoice: "every" | "every_interval" | "on_the_xth" | "other" =
"other";
let minutesChoice:
| "every"
| "every_interval"
| "on_the_xth"
| "other"
| "has_seconds" = "other";
let hoursChoice:
| "every"
| "every_interval"
| "on_the_xth"
| "other"
| "has_seconds_or_minutes" = "other";
let seconds = 0;
let minutes = 0;
let hours = 0;
if (trigger.seconds !== undefined) {
const seconds_all = trigger.seconds === "*";
const seconds_interval =
typeof trigger.seconds === "string" && trigger.seconds.startsWith("/");
seconds = seconds_all
? 0
: typeof trigger.seconds === "number"
? trigger.seconds
: seconds_interval
? parseInt(trigger.seconds.substring(1))
: parseInt(trigger.seconds);
if (
isNaN(seconds) ||
seconds > 59 ||
seconds < 0 ||
(seconds_interval && seconds === 0)
) {
invalidParts.push("seconds");
}
if (seconds_all || (seconds_interval && seconds === 1)) {
secondsChoice = "every";
} else if (seconds_interval) {
secondsChoice = "every_interval";
} else {
secondsChoice = "on_the_xth";
}
}
if (trigger.minutes !== undefined) {
const minutes_all = trigger.minutes === "*";
const minutes_interval =
typeof trigger.minutes === "string" && trigger.minutes.startsWith("/");
minutes = minutes_all
? 0
: typeof trigger.minutes === "number"
? trigger.minutes
: minutes_interval
? parseInt(trigger.minutes.substring(1))
: parseInt(trigger.minutes);
if (
isNaN(minutes) ||
minutes > 59 ||
minutes < 0 ||
(minutes_interval && minutes === 0)
) {
invalidParts.push("minutes");
}
if (minutes_all || (minutes_interval && minutes === 1)) {
minutesChoice = "every";
} else if (minutes_interval) {
minutesChoice = "every_interval";
} else {
minutesChoice =
trigger.seconds !== undefined ? "has_seconds" : "on_the_xth";
}
} else if (trigger.seconds !== undefined) {
if (trigger.hours !== undefined) {
minutes = 0;
minutesChoice = "has_seconds";
} else {
minutesChoice = "every";
}
}
if (trigger.hours !== undefined) {
const hours_all = trigger.hours === "*";
const hours_interval =
typeof trigger.hours === "string" && trigger.hours.startsWith("/");
hours = hours_all
? 0
: typeof trigger.hours === "number"
? trigger.hours
: hours_interval
? parseInt(trigger.hours.substring(1))
: parseInt(trigger.hours);
if (
isNaN(hours) ||
hours > 23 ||
hours < 0 ||
(hours_interval && hours === 0)
) {
invalidParts.push("hours");
}
if (hours_all || (hours_interval && hours === 1)) {
hoursChoice = "every";
} else if (hours_interval) {
hoursChoice = "every_interval";
} else {
hoursChoice =
trigger.seconds !== undefined || trigger.minutes !== undefined
? "has_seconds_or_minutes"
: "on_the_xth";
}
} else {
hoursChoice = "every";
}
if (invalidParts.length !== 0) {
return hass.localize(
`${triggerTranslationBaseKey}.time_pattern.description.invalid`,
{
parts: formatListWithAnds(
hass.locale,
invalidParts.map((invalidPart) =>
hass.localize(
`${triggerTranslationBaseKey}.time_pattern.${invalidPart}`
)
)
),
}
);
}
return hass.localize(
`${triggerTranslationBaseKey}.time_pattern.description.full`,
{
secondsChoice: secondsChoice,
minutesChoice: minutesChoice,
hoursChoice: hoursChoice,
seconds: seconds,
minutes: minutes,
hours: hours,
secondsWithOrdinal: hass.localize(
`${triggerTranslationBaseKey}.time_pattern.description.ordinal`,
{
part: seconds,
}
),
minutesWithOrdinal: hass.localize(
`${triggerTranslationBaseKey}.time_pattern.description.ordinal`,
{
part: minutes,
}
),
hoursWithOrdinal: hass.localize(
`${triggerTranslationBaseKey}.time_pattern.description.ordinal`,
{
part: hours,
}
),
}
);
}
// Zone Trigger
if (trigger.trigger === "zone" && trigger.entity_id && trigger.zone) {
const entities: string[] = [];
const zones: string[] = [];
const states = hass.states;
if (Array.isArray(trigger.entity_id)) {
for (const entity of trigger.entity_id.values()) {
if (states[entity]) {
entities.push(computeStateName(states[entity]) || entity);
}
}
} else {
entities.push(
states[trigger.entity_id]
? computeStateName(states[trigger.entity_id])
: trigger.entity_id
);
}
if (Array.isArray(trigger.zone)) {
for (const zone of trigger.zone.values()) {
if (states[zone]) {
zones.push(computeStateName(states[zone]) || zone);
}
}
} else {
zones.push(
states[trigger.zone]
? computeStateName(states[trigger.zone])
: trigger.zone
);
}
return hass.localize(`${triggerTranslationBaseKey}.zone.description.full`, {
entity: formatListWithOrs(hass.locale, entities),
event: trigger.event.toString(),
zone: formatListWithOrs(hass.locale, zones),
numberOfZones: zones.length,
});
}
// Geo Location Trigger
if (trigger.trigger === "geo_location" && trigger.source && trigger.zone) {
const sources: string[] = [];
const zones: string[] = [];
const states = hass.states;
if (Array.isArray(trigger.source)) {
for (const source of trigger.source.values()) {
sources.push(source);
}
} else {
sources.push(trigger.source);
}
if (Array.isArray(trigger.zone)) {
for (const zone of trigger.zone.values()) {
if (states[zone]) {
zones.push(computeStateName(states[zone]) || zone);
}
}
} else {
zones.push(
states[trigger.zone]
? computeStateName(states[trigger.zone])
: trigger.zone
);
}
return hass.localize(
`${triggerTranslationBaseKey}.geo_location.description.full`,
{
source: formatListWithOrs(hass.locale, sources),
event: trigger.event.toString(),
zone: formatListWithOrs(hass.locale, zones),
numberOfZones: zones.length,
}
);
}
// MQTT Trigger
if (trigger.trigger === "mqtt") {
return hass.localize(`${triggerTranslationBaseKey}.mqtt.description.full`);
}
// Template Trigger
if (trigger.trigger === "template") {
let duration = "";
if (trigger.for) {
duration = describeDuration(hass.locale, trigger.for) ?? "";
}
return hass.localize(
`${triggerTranslationBaseKey}.template.description.full`,
{ hasDuration: duration !== "" ? "true" : "false", duration: duration }
);
}
// Webhook Trigger
if (trigger.trigger === "webhook") {
return hass.localize(
`${triggerTranslationBaseKey}.webhook.description.full`
);
}
// Conversation Trigger
if (trigger.trigger === "conversation") {
if (!trigger.command || !trigger.command.length) {
return hass.localize(
`${triggerTranslationBaseKey}.conversation.description.empty`
);
}
const commands = ensureArray(trigger.command);
if (commands.length === 1) {
return hass.localize(
`${triggerTranslationBaseKey}.conversation.description.single`,
{
sentence: commands[0],
}
);
}
return hass.localize(
`${triggerTranslationBaseKey}.conversation.description.multiple`,
{
sentence: commands[0],
count: commands.length - 1,
}
);
}
// Persistent Notification Trigger
if (trigger.trigger === "persistent_notification") {
return hass.localize(
`${triggerTranslationBaseKey}.persistent_notification.description.full`
);
}
// Device Trigger
if (trigger.trigger === "device" && trigger.device_id) {
const config = trigger as DeviceTrigger;
const localized = localizeDeviceAutomationTrigger(
hass,
entityRegistry,
config
);
if (localized) {
return localized;
}
const stateObj = hass.states[config.entity_id as string] as
| HassEntity
| undefined;
return `${stateObj ? computeStateName(stateObj) : config.entity_id} ${
config.type
}`;
}
// Calendar Trigger
if (trigger.trigger === "calendar") {
const calendarEntity = hass.states[trigger.entity_id]
? computeStateName(hass.states[trigger.entity_id])
: trigger.entity_id;
let offsetChoice = "other";
let offset = "";
if (trigger.offset) {
offsetChoice = trigger.offset.startsWith("-") ? "before" : "after";
const parts = trigger.offset.startsWith("-")
? trigger.offset.substring(1).split(":")
: trigger.offset.split(":");
const duration = {
hours: parts.length > 0 ? +parts[0] : 0,
minutes: parts.length > 1 ? +parts[1] : 0,
seconds: parts.length > 2 ? +parts[2] : 0,
};
offset = formatDurationLong(hass.locale, duration);
if (offset === "") {
offsetChoice = "other";
}
}
return hass.localize(
`${triggerTranslationBaseKey}.calendar.description.full`,
{
eventChoice: trigger.event,
offsetChoice: offsetChoice,
offset: offset,
hasCalendar: trigger.entity_id ? "true" : "false",
calendar: calendarEntity,
}
);
}
return undefined;
};
export const describeCondition = (
condition: Condition,
hass: HomeAssistant,
entityRegistry: EntityRegistryEntry[],
ignoreAlias = false
): string => {
try {
const description = tryDescribeCondition(
condition,
hass,
entityRegistry,
ignoreAlias
);
if (typeof description !== "string") {
throw new Error(String(description));
}
return description;
} catch (error: any) {
// eslint-disable-next-line no-console
console.error(error);
let msg = "Error in describing condition";
if (error.message) {
msg += ": " + error.message;
}
return msg;
}
};
const tryDescribeCondition = (
condition: Condition,
hass: HomeAssistant,
entityRegistry: EntityRegistryEntry[],
ignoreAlias = false
) => {
if (typeof condition === "string" && hasTemplate(condition)) {
return hass.localize(
`${conditionsTranslationBaseKey}.template.description.full`
);
}
if (condition.alias && !ignoreAlias) {
return condition.alias;
}
if (!condition.condition) {
const shorthands: ("and" | "or" | "not")[] = ["and", "or", "not"];
for (const key of shorthands) {
if (!(key in condition)) {
continue;
}
if (ensureArray(condition[key])) {
condition = {
condition: key,
conditions: condition[key],
};
}
}
}
const description = describeLegacyCondition(
condition as LegacyCondition,
hass,
entityRegistry
);
if (description) {
return description;
}
const conditionType = condition.condition;
const domain = getConditionDomain(condition.condition);
const type = getConditionObjectId(condition.condition);
return (
hass.localize(`component.${domain}.conditions.${type}.name`) ||
hass.localize(
`ui.panel.config.automation.editor.conditions.type.${conditionType as LegacyCondition["condition"]}.label`
) ||
hass.localize(
`ui.panel.config.automation.editor.conditions.unknown_condition`
)
);
};
const describeLegacyCondition = (
condition: LegacyCondition,
hass: HomeAssistant,
entityRegistry: EntityRegistryEntry[]
) => {
if (condition.condition === "or") {
const conditions = ensureArray(condition.conditions);
if (!conditions || conditions.length === 0) {
return hass.localize(
`${conditionsTranslationBaseKey}.or.description.no_conditions`
);
}
const count = conditions.length;
return hass.localize(
`${conditionsTranslationBaseKey}.or.description.full`,
{
count: count,
}
);
}
if (condition.condition === "and") {
const conditions = ensureArray(condition.conditions);
if (!conditions || conditions.length === 0) {
return hass.localize(
`${conditionsTranslationBaseKey}.and.description.no_conditions`
);
}
const count = conditions.length;
return hass.localize(
`${conditionsTranslationBaseKey}.and.description.full`,
{
count: count,
}
);
}
if (condition.condition === "not") {
const conditions = ensureArray(condition.conditions);
if (!conditions || conditions.length === 0) {
return hass.localize(
`${conditionsTranslationBaseKey}.not.description.no_conditions`
);
}
if (conditions.length === 1) {
return hass.localize(
`${conditionsTranslationBaseKey}.not.description.one_condition`
);
}
return hass.localize(
`${conditionsTranslationBaseKey}.not.description.full`,
{ count: conditions.length }
);
}
// State Condition
if (condition.condition === "state") {
if (!condition.entity_id) {
return hass.localize(
`${conditionsTranslationBaseKey}.state.description.no_entity`
);
}
let attribute = "";
if (condition.attribute) {
const stateObj = Array.isArray(condition.entity_id)
? hass.states[condition.entity_id[0]]
: (hass.states[condition.entity_id] as HassEntity | undefined);
attribute = stateObj
? computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
condition.attribute
)
: condition.attribute;
}
const entities: string[] = [];
if (Array.isArray(condition.entity_id)) {
for (const entity of condition.entity_id.values()) {
if (hass.states[entity]) {
entities.push(computeStateName(hass.states[entity]) || entity);
}
}
} else if (condition.entity_id) {
entities.push(
hass.states[condition.entity_id]
? computeStateName(hass.states[condition.entity_id])
: condition.entity_id
);
}
const states: string[] = [];
const stateObj = hass.states[
Array.isArray(condition.entity_id)
? condition.entity_id[0]
: condition.entity_id
] as HassEntity | undefined;
if (Array.isArray(condition.state)) {
for (const state of condition.state.values()) {
states.push(
stateObj
? condition.attribute
? hass
.formatEntityAttributeValue(
stateObj,
condition.attribute,
state
)
.toString()
: hass.formatEntityState(stateObj, state)
: state
);
}
} else if (condition.state !== "") {
states.push(
stateObj
? condition.attribute
? hass
.formatEntityAttributeValue(
stateObj,
condition.attribute,
condition.state
)
.toString()
: hass.formatEntityState(stateObj, condition.state.toString())
: condition.state.toString()
);
}
let duration = "";
if (condition.for) {
duration = describeDuration(hass.locale, condition.for) || "";
}
return hass.localize(
`${conditionsTranslationBaseKey}.state.description.full`,
{
hasAttribute: attribute !== "" ? "true" : "false",
attribute: attribute,
numberOfEntities: entities.length,
entities:
condition.match === "any"
? formatListWithOrs(hass.locale, entities)
: formatListWithAnds(hass.locale, entities),
numberOfStates: states.length,
states: formatListWithOrs(hass.locale, states),
hasDuration: duration !== "" ? "true" : "false",
duration: duration,
}
);
}
// Numeric State Condition
if (condition.condition === "numeric_state" && condition.entity_id) {
const entity_ids = ensureArray(condition.entity_id);
const stateObj = hass.states[entity_ids[0]] as HassEntity | undefined;
const entity = formatListWithAnds(
hass.locale,
entity_ids.map((id) =>
hass.states[id] ? computeStateName(hass.states[id]) : id || ""
)
);
const attribute = condition.attribute
? stateObj
? computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
condition.attribute
)
: condition.attribute
: undefined;
if (condition.above !== undefined && condition.below !== undefined) {
return hass.localize(
`${conditionsTranslationBaseKey}.numeric_state.description.above-below`,
{
attribute,
entity,
numberOfEntities: entity_ids.length,
above: condition.above,
below: condition.below,
}
);
}
if (condition.above !== undefined) {
return hass.localize(
`${conditionsTranslationBaseKey}.numeric_state.description.above`,
{
attribute,
entity,
numberOfEntities: entity_ids.length,
above: condition.above,
}
);
}
if (condition.below !== undefined) {
return hass.localize(
`${conditionsTranslationBaseKey}.numeric_state.description.below`,
{
attribute,
entity,
numberOfEntities: entity_ids.length,
below: condition.below,
}
);
}
}
// Time condition
if (condition.condition === "time") {
const weekdaysArray = ensureArray(condition.weekday);
const validWeekdays =
weekdaysArray && weekdaysArray.length > 0 && weekdaysArray.length < 7;
if (condition.before || condition.after || validWeekdays) {
const before =
typeof condition.before !== "string"
? condition.before
: condition.before.includes(".")
? `entity ${
hass.states[condition.before]
? computeStateName(hass.states[condition.before])
: condition.before
}`
: localizeTimeString(condition.before, hass.locale, hass.config);
const after =
typeof condition.after !== "string"
? condition.after
: condition.after.includes(".")
? `entity ${
hass.states[condition.after]
? computeStateName(hass.states[condition.after])
: condition.after
}`
: localizeTimeString(condition.after, hass.locale, hass.config);
let localizedDays: string[] = [];
if (validWeekdays) {
localizedDays = weekdaysArray.map((d) =>
hass.localize(
`ui.panel.config.automation.editor.conditions.type.time.weekdays.${d}`
)
);
}
let hasTime = "";
if (after !== undefined && before !== undefined) {
hasTime = "after_before";
} else if (after !== undefined) {
hasTime = "after";
} else if (before !== undefined) {
hasTime = "before";
}
return hass.localize(
`${conditionsTranslationBaseKey}.time.description.full`,
{
hasTime: hasTime,
hasTimeAndDay: (after || before) && validWeekdays ? "true" : "false",
hasDay: validWeekdays ? "true" : "false",
time_before: before,
time_after: after,
day: formatListWithOrs(hass.locale, localizedDays),
}
);
}
}
// Sun condition
if (condition.condition === "sun" && (condition.before || condition.after)) {
let afterDuration = "";
if (condition.after && condition.after_offset) {
if (typeof condition.after_offset === "number") {
afterDuration = secondsToDuration(condition.after_offset)!;
} else if (typeof condition.after_offset === "string") {
afterDuration = condition.after_offset;
} else {
afterDuration = JSON.stringify(condition.after_offset);
}
}
let beforeDuration = "";
if (condition.before && condition.before_offset) {
if (typeof condition.before_offset === "number") {
beforeDuration = secondsToDuration(condition.before_offset)!;
} else if (typeof condition.before_offset === "string") {
beforeDuration = condition.before_offset;
} else {
beforeDuration = JSON.stringify(condition.before_offset);
}
}
return hass.localize(
`${conditionsTranslationBaseKey}.sun.description.full`,
{
afterChoice: condition.after ?? "other",
afterOffsetChoice: afterDuration !== "" ? "offset" : "other",
afterOffset: afterDuration,
beforeChoice: condition.before ?? "other",
beforeOffsetChoice: beforeDuration !== "" ? "offset" : "other",
beforeOffset: beforeDuration,
}
);
}
// Zone condition
if (condition.condition === "zone" && condition.entity_id && condition.zone) {
const entities: string[] = [];
const zones: string[] = [];
const states = hass.states;
if (Array.isArray(condition.entity_id)) {
for (const entity of condition.entity_id.values()) {
if (states[entity]) {
entities.push(computeStateName(states[entity]) || entity);
}
}
} else {
entities.push(
states[condition.entity_id]
? computeStateName(states[condition.entity_id])
: condition.entity_id
);
}
if (Array.isArray(condition.zone)) {
for (const zone of condition.zone.values()) {
if (states[zone]) {
zones.push(computeStateName(states[zone]) || zone);
}
}
} else {
zones.push(
states[condition.zone]
? computeStateName(states[condition.zone])
: condition.zone
);
}
const entitiesString = formatListWithOrs(hass.locale, entities);
const zonesString = formatListWithOrs(hass.locale, zones);
return hass.localize(
`${conditionsTranslationBaseKey}.zone.description.full`,
{
entity: entitiesString,
numberOfEntities: entities.length,
zone: zonesString,
numberOfZones: zones.length,
}
);
}
if (condition.condition === "device" && condition.device_id) {
const config = condition as DeviceCondition;
const localized = localizeDeviceAutomationCondition(
hass,
entityRegistry,
config
);
if (localized) {
return localized;
}
const stateObj = hass.states[config.entity_id as string] as
| HassEntity
| undefined;
return `${stateObj ? computeStateName(stateObj) : config.entity_id} ${
config.type
}`;
}
if (condition.condition === "template") {
return hass.localize(
`${conditionsTranslationBaseKey}.template.description.full`
);
}
if (condition.condition === "trigger" && condition.id != null) {
return hass.localize(
`${conditionsTranslationBaseKey}.trigger.description.full`,
{
id: formatListWithOrs(
hass.locale,
ensureArray(condition.id).map((id) => id.toString())
),
}
);
}
return undefined;
};