mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 19:26:36 +00:00
20240102.0 (#19234)
This commit is contained in:
commit
386c3ea1ca
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20240101.0"
|
version = "20240102.0"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -29,6 +29,7 @@ import {
|
|||||||
mdiFlash,
|
mdiFlash,
|
||||||
mdiFlower,
|
mdiFlower,
|
||||||
mdiFormatListBulleted,
|
mdiFormatListBulleted,
|
||||||
|
mdiFormatListCheckbox,
|
||||||
mdiFormTextbox,
|
mdiFormTextbox,
|
||||||
mdiGauge,
|
mdiGauge,
|
||||||
mdiGoogleAssistant,
|
mdiGoogleAssistant,
|
||||||
@ -64,6 +65,7 @@ import {
|
|||||||
mdiTransmissionTower,
|
mdiTransmissionTower,
|
||||||
mdiWater,
|
mdiWater,
|
||||||
mdiWaterPercent,
|
mdiWaterPercent,
|
||||||
|
mdiWeatherPartlyCloudy,
|
||||||
mdiWeatherPouring,
|
mdiWeatherPouring,
|
||||||
mdiWeatherRainy,
|
mdiWeatherRainy,
|
||||||
mdiWeatherWindy,
|
mdiWeatherWindy,
|
||||||
@ -128,6 +130,7 @@ export const FIXED_DOMAIN_ICONS = {
|
|||||||
updater: mdiCloudUpload,
|
updater: mdiCloudUpload,
|
||||||
vacuum: mdiRobotVacuum,
|
vacuum: mdiRobotVacuum,
|
||||||
wake_word: mdiChatSleep,
|
wake_word: mdiChatSleep,
|
||||||
|
weather: mdiWeatherPartlyCloudy,
|
||||||
zone: mdiMapMarkerRadius,
|
zone: mdiMapMarkerRadius,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -166,6 +169,7 @@ export const FIXED_DEVICE_CLASS_ICONS = {
|
|||||||
precipitation_intensity: mdiWeatherPouring,
|
precipitation_intensity: mdiWeatherPouring,
|
||||||
pressure: mdiGauge,
|
pressure: mdiGauge,
|
||||||
reactive_power: mdiFlash,
|
reactive_power: mdiFlash,
|
||||||
|
shopping_List: mdiFormatListCheckbox,
|
||||||
signal_strength: mdiWifi,
|
signal_strength: mdiWifi,
|
||||||
sound_pressure: mdiEarHearing,
|
sound_pressure: mdiEarHearing,
|
||||||
speed: mdiSpeedometer,
|
speed: mdiSpeedometer,
|
||||||
|
@ -10,9 +10,9 @@ class HaLabeledSlider extends LitElement {
|
|||||||
|
|
||||||
@property() public caption?: string;
|
@property() public caption?: string;
|
||||||
|
|
||||||
@property() public disabled?: boolean;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property() public required?: boolean;
|
@property({ type: Boolean }) public required = true;
|
||||||
|
|
||||||
@property() public min: number = 0;
|
@property() public min: number = 0;
|
||||||
|
|
||||||
|
@ -4,7 +4,14 @@ import {
|
|||||||
HassServices,
|
HassServices,
|
||||||
HassServiceTarget,
|
HassServiceTarget,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
nothing,
|
||||||
|
} from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { ensureArray } from "../common/array/ensure-array";
|
import { ensureArray } from "../common/array/ensure-array";
|
||||||
@ -83,6 +90,8 @@ export class HaServiceControl extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public showAdvanced?: boolean;
|
@property({ type: Boolean }) public showAdvanced?: boolean;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true }) public hidePicker?: boolean;
|
||||||
|
|
||||||
@state() private _value!: this["value"];
|
@state() private _value!: this["value"];
|
||||||
|
|
||||||
@state() private _checkedKeys = new Set();
|
@state() private _checkedKeys = new Set();
|
||||||
@ -363,12 +372,14 @@ export class HaServiceControl extends LitElement {
|
|||||||
)) ||
|
)) ||
|
||||||
serviceData?.description;
|
serviceData?.description;
|
||||||
|
|
||||||
return html`<ha-service-picker
|
return html`${this.hidePicker
|
||||||
|
? nothing
|
||||||
|
: html`<ha-service-picker
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._value?.service}
|
.value=${this._value?.service}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@value-changed=${this._serviceChanged}
|
@value-changed=${this._serviceChanged}
|
||||||
></ha-service-picker>
|
></ha-service-picker>`}
|
||||||
<div class="description">
|
<div class="description">
|
||||||
${description ? html`<p>${description}</p>` : ""}
|
${description ? html`<p>${description}</p>` : ""}
|
||||||
${this._manifest
|
${this._manifest
|
||||||
@ -735,6 +746,9 @@ export class HaServiceControl extends LitElement {
|
|||||||
margin: var(--service-control-padding, 0 16px);
|
margin: var(--service-control-padding, 0 16px);
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
}
|
}
|
||||||
|
:host([hidePicker]) p {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
.checkbox-spacer {
|
.checkbox-spacer {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,6 @@ export const CONDITION_GROUPS: AutomationElementGroup = {
|
|||||||
icon: mdiDotsHorizontal,
|
icon: mdiDotsHorizontal,
|
||||||
members: {
|
members: {
|
||||||
template: {},
|
template: {},
|
||||||
trigger: {},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -52,6 +52,7 @@ export const serviceActionStruct: Describe<ServiceAction> = assign(
|
|||||||
target: optional(targetStruct),
|
target: optional(targetStruct),
|
||||||
data: optional(object()),
|
data: optional(object()),
|
||||||
response_variable: optional(string()),
|
response_variable: optional(string()),
|
||||||
|
metadata: optional(object()),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -133,6 +134,7 @@ export interface ServiceAction extends BaseAction {
|
|||||||
target?: HassServiceTarget;
|
target?: HassServiceTarget;
|
||||||
data?: Record<string, unknown>;
|
data?: Record<string, unknown>;
|
||||||
response_variable?: string;
|
response_variable?: string;
|
||||||
|
metadata?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceAction extends BaseAction {
|
export interface DeviceAction extends BaseAction {
|
||||||
|
@ -168,6 +168,18 @@ const tryDescribeAction = <T extends ActionType>(
|
|||||||
const service =
|
const service =
|
||||||
hass.localize(`component.${domain}.services.${serviceName}.name`) ||
|
hass.localize(`component.${domain}.services.${serviceName}.name`) ||
|
||||||
hass.services[domain][serviceName]?.name;
|
hass.services[domain][serviceName]?.name;
|
||||||
|
|
||||||
|
if (config.metadata) {
|
||||||
|
return hass.localize(
|
||||||
|
`${actionTranslationBaseKey}.service.description.service_name`,
|
||||||
|
{
|
||||||
|
domain: domainToName(hass.localize, domain),
|
||||||
|
name: service || config.service,
|
||||||
|
targets: formatListWithAnds(hass.locale, targets),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return hass.localize(
|
return hass.localize(
|
||||||
`${actionTranslationBaseKey}.service.description.service_based_on_name`,
|
`${actionTranslationBaseKey}.service.description.service_based_on_name`,
|
||||||
{
|
{
|
||||||
@ -404,7 +416,9 @@ const tryDescribeAction = <T extends ActionType>(
|
|||||||
if (actionType === "device_action") {
|
if (actionType === "device_action") {
|
||||||
const config = action as DeviceAction;
|
const config = action as DeviceAction;
|
||||||
if (!config.device_id) {
|
if (!config.device_id) {
|
||||||
return "Device action";
|
return hass.localize(
|
||||||
|
`${actionTranslationBaseKey}.device_id.description.no_device`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const localized = localizeDeviceAutomationAction(
|
const localized = localizeDeviceAutomationAction(
|
||||||
hass,
|
hass,
|
||||||
|
@ -144,9 +144,9 @@ class DialogCalendarEventEditor extends LitElement {
|
|||||||
escapeKeyAction
|
escapeKeyAction
|
||||||
.heading=${createCloseHeading(
|
.heading=${createCloseHeading(
|
||||||
this.hass,
|
this.hass,
|
||||||
isCreate
|
this.hass.localize(
|
||||||
? this.hass.localize("ui.components.calendar.event.add")
|
`ui.components.calendar.event.${isCreate ? "add" : "edit"}`
|
||||||
: this._summary
|
)
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@ -29,6 +29,8 @@ import { classMap } from "lit/directives/class-map";
|
|||||||
import { storage } from "../../../../common/decorators/storage";
|
import { storage } from "../../../../common/decorators/storage";
|
||||||
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 { computeDomain } from "../../../../common/entity/compute_domain";
|
||||||
|
import { domainIconWithoutDefault } from "../../../../common/entity/domain_icon";
|
||||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
import "../../../../components/ha-alert";
|
import "../../../../components/ha-alert";
|
||||||
@ -190,7 +192,13 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
<h3 slot="header">
|
<h3 slot="header">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="action-icon"
|
class="action-icon"
|
||||||
.path=${ACTION_ICONS[type!]}
|
.path=${type === "service" &&
|
||||||
|
"service" in this.action &&
|
||||||
|
this.action.service
|
||||||
|
? domainIconWithoutDefault(
|
||||||
|
computeDomain(this.action.service as string)
|
||||||
|
) || ACTION_ICONS[type!]
|
||||||
|
: ACTION_ICONS[type!]}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${capitalizeFirstLetter(
|
${capitalizeFirstLetter(
|
||||||
describeAction(this.hass, this._entityReg, this.action)
|
describeAction(this.hass, this._entityReg, this.action)
|
||||||
|
@ -191,6 +191,7 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
} else if (isService(action)) {
|
} else if (isService(action)) {
|
||||||
actions = this.actions.concat({
|
actions = this.actions.concat({
|
||||||
service: getService(action),
|
service: getService(action),
|
||||||
|
metadata: {},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const elClass = customElements.get(
|
const elClass = customElements.get(
|
||||||
|
@ -1,14 +1,21 @@
|
|||||||
import "@material/mwc-list/mwc-list";
|
import "@material/mwc-list/mwc-list";
|
||||||
import { mdiClose, mdiContentPaste, mdiPlus } from "@mdi/js";
|
import { mdiClose, mdiContentPaste, mdiPlus } from "@mdi/js";
|
||||||
import Fuse, { IFuseOptions } from "fuse.js";
|
import Fuse, { IFuseOptions } from "fuse.js";
|
||||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
import {
|
||||||
|
CSSResultGroup,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
nothing,
|
||||||
|
} from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { domainIcon } from "../../../common/entity/domain_icon";
|
import { domainIconWithoutDefault } from "../../../common/entity/domain_icon";
|
||||||
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
||||||
import { stringCompare } from "../../../common/string/compare";
|
import { stringCompare } from "../../../common/string/compare";
|
||||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
@ -38,10 +45,13 @@ import { TRIGGER_GROUPS, TRIGGER_ICONS } from "../../../data/trigger";
|
|||||||
import { HassDialog } from "../../../dialogs/make-dialog-manager";
|
import { HassDialog } from "../../../dialogs/make-dialog-manager";
|
||||||
import { haStyle, haStyleDialog } from "../../../resources/styles";
|
import { haStyle, haStyleDialog } from "../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { brandsUrl } from "../../../util/brands-url";
|
||||||
import {
|
import {
|
||||||
AddAutomationElementDialogParams,
|
AddAutomationElementDialogParams,
|
||||||
PASTE_VALUE,
|
PASTE_VALUE,
|
||||||
} from "./show-add-automation-element-dialog";
|
} from "./show-add-automation-element-dialog";
|
||||||
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
|
import { deepEqual } from "../../../common/util/deep-equal";
|
||||||
|
|
||||||
const TYPES = {
|
const TYPES = {
|
||||||
trigger: { groups: TRIGGER_GROUPS, icons: TRIGGER_ICONS },
|
trigger: { groups: TRIGGER_GROUPS, icons: TRIGGER_ICONS },
|
||||||
@ -59,7 +69,8 @@ interface ListItem {
|
|||||||
key: string;
|
key: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
icon: string;
|
icon?: string;
|
||||||
|
image?: string;
|
||||||
group: boolean;
|
group: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +90,8 @@ const ENTITY_DOMAINS_OTHER = new Set([
|
|||||||
"image_processing",
|
"image_processing",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const ENTITY_DOMAINS_MAIN = new Set(["notify"]);
|
||||||
|
|
||||||
@customElement("add-automation-element-dialog")
|
@customElement("add-automation-element-dialog")
|
||||||
class DialogAddAutomationElement extends LitElement implements HassDialog {
|
class DialogAddAutomationElement extends LitElement implements HassDialog {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -93,13 +106,15 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
|
|
||||||
@state() private _manifests?: DomainManifestLookup;
|
@state() private _manifests?: DomainManifestLookup;
|
||||||
|
|
||||||
|
@state() private _domains?: Set<string>;
|
||||||
|
|
||||||
@query("ha-dialog") private _dialog?: HaDialog;
|
@query("ha-dialog") private _dialog?: HaDialog;
|
||||||
|
|
||||||
private _fullScreen = false;
|
private _fullScreen = false;
|
||||||
|
|
||||||
private _width?: number;
|
@state() private _width?: number;
|
||||||
|
|
||||||
private _height?: number;
|
@state() private _height?: number;
|
||||||
|
|
||||||
public showDialog(params): void {
|
public showDialog(params): void {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
@ -124,6 +139,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
this._prev = undefined;
|
this._prev = undefined;
|
||||||
this._filter = "";
|
this._filter = "";
|
||||||
this._manifests = undefined;
|
this._manifests = undefined;
|
||||||
|
this._domains = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _convertToItem = (
|
private _convertToItem = (
|
||||||
@ -152,6 +168,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
private _getFilteredItems = memoizeOne(
|
private _getFilteredItems = memoizeOne(
|
||||||
(
|
(
|
||||||
type: AddAutomationElementDialogParams["type"],
|
type: AddAutomationElementDialogParams["type"],
|
||||||
|
root: AddAutomationElementDialogParams["root"],
|
||||||
group: string | undefined,
|
group: string | undefined,
|
||||||
filter: string,
|
filter: string,
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
@ -164,6 +181,10 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
: TYPES[type].groups[group].members!
|
: TYPES[type].groups[group].members!
|
||||||
: TYPES[type].groups;
|
: TYPES[type].groups;
|
||||||
|
|
||||||
|
if (type === "condition" && group === "other" && !root) {
|
||||||
|
groups.trigger = {};
|
||||||
|
}
|
||||||
|
|
||||||
const flattenGroups = (grp: AutomationElementGroup) =>
|
const flattenGroups = (grp: AutomationElementGroup) =>
|
||||||
Object.entries(grp).map(([key, options]) =>
|
Object.entries(grp).map(([key, options]) =>
|
||||||
options.members
|
options.members
|
||||||
@ -191,7 +212,9 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
private _getGroupItems = memoizeOne(
|
private _getGroupItems = memoizeOne(
|
||||||
(
|
(
|
||||||
type: AddAutomationElementDialogParams["type"],
|
type: AddAutomationElementDialogParams["type"],
|
||||||
|
root: AddAutomationElementDialogParams["root"],
|
||||||
group: string | undefined,
|
group: string | undefined,
|
||||||
|
domains: Set<string> | undefined,
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
services: HomeAssistant["services"],
|
services: HomeAssistant["services"],
|
||||||
manifests?: DomainManifestLookup
|
manifests?: DomainManifestLookup
|
||||||
@ -208,6 +231,10 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
? TYPES[type].groups[group].members!
|
? TYPES[type].groups[group].members!
|
||||||
: TYPES[type].groups;
|
: TYPES[type].groups;
|
||||||
|
|
||||||
|
if (type === "condition" && group === "other" && !root) {
|
||||||
|
groups.trigger = {};
|
||||||
|
}
|
||||||
|
|
||||||
const result = Object.entries(groups).map(([key, options]) =>
|
const result = Object.entries(groups).map(([key, options]) =>
|
||||||
this._convertToItem(key, options, type, localize)
|
this._convertToItem(key, options, type, localize)
|
||||||
);
|
);
|
||||||
@ -215,15 +242,33 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
if (type === "action") {
|
if (type === "action") {
|
||||||
if (!this._group) {
|
if (!this._group) {
|
||||||
result.unshift(
|
result.unshift(
|
||||||
...this._serviceGroups(localize, services, manifests, undefined)
|
...this._serviceGroups(
|
||||||
|
localize,
|
||||||
|
services,
|
||||||
|
manifests,
|
||||||
|
domains,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
);
|
);
|
||||||
} else if (this._group === "helpers") {
|
} else if (this._group === "helpers") {
|
||||||
result.unshift(
|
result.unshift(
|
||||||
...this._serviceGroups(localize, services, manifests, "helper")
|
...this._serviceGroups(
|
||||||
|
localize,
|
||||||
|
services,
|
||||||
|
manifests,
|
||||||
|
domains,
|
||||||
|
"helper"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
} else if (this._group === "other") {
|
} else if (this._group === "other") {
|
||||||
result.unshift(
|
result.unshift(
|
||||||
...this._serviceGroups(localize, services, manifests, "other")
|
...this._serviceGroups(
|
||||||
|
localize,
|
||||||
|
services,
|
||||||
|
manifests,
|
||||||
|
domains,
|
||||||
|
"other"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,11 +288,11 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
private _serviceGroups = memoizeOne(
|
private _serviceGroups = (
|
||||||
(
|
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
services: HomeAssistant["services"],
|
services: HomeAssistant["services"],
|
||||||
manifests: DomainManifestLookup | undefined,
|
manifests: DomainManifestLookup | undefined,
|
||||||
|
domains: Set<string> | undefined,
|
||||||
type: "helper" | "other" | undefined
|
type: "helper" | "other" | undefined
|
||||||
): ListItem[] => {
|
): ListItem[] => {
|
||||||
if (!services || !manifests) {
|
if (!services || !manifests) {
|
||||||
@ -256,18 +301,31 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
const result: ListItem[] = [];
|
const result: ListItem[] = [];
|
||||||
Object.keys(services).forEach((domain) => {
|
Object.keys(services).forEach((domain) => {
|
||||||
const manifest = manifests[domain];
|
const manifest = manifests[domain];
|
||||||
|
const domainUsed = !domains ? true : domains.has(domain);
|
||||||
if (
|
if (
|
||||||
(type === undefined &&
|
(type === undefined &&
|
||||||
manifest?.integration_type === "entity" &&
|
(ENTITY_DOMAINS_MAIN.has(domain) ||
|
||||||
!ENTITY_DOMAINS_OTHER.has(domain)) ||
|
(manifest?.integration_type === "entity" &&
|
||||||
|
domainUsed &&
|
||||||
|
!ENTITY_DOMAINS_OTHER.has(domain)))) ||
|
||||||
(type === "helper" && manifest?.integration_type === "helper") ||
|
(type === "helper" && manifest?.integration_type === "helper") ||
|
||||||
(type === "other" &&
|
(type === "other" &&
|
||||||
|
!ENTITY_DOMAINS_MAIN.has(domain) &&
|
||||||
(ENTITY_DOMAINS_OTHER.has(domain) ||
|
(ENTITY_DOMAINS_OTHER.has(domain) ||
|
||||||
|
(!domainUsed && manifest?.integration_type === "entity") ||
|
||||||
!["helper", "entity"].includes(manifest?.integration_type || "")))
|
!["helper", "entity"].includes(manifest?.integration_type || "")))
|
||||||
) {
|
) {
|
||||||
|
const icon = domainIconWithoutDefault(domain);
|
||||||
result.push({
|
result.push({
|
||||||
group: true,
|
group: true,
|
||||||
icon: domainIcon(domain),
|
icon,
|
||||||
|
image: !icon
|
||||||
|
? brandsUrl({
|
||||||
|
domain,
|
||||||
|
type: "icon",
|
||||||
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
|
})
|
||||||
|
: undefined,
|
||||||
key: `${SERVICE_PREFIX}${domain}`,
|
key: `${SERVICE_PREFIX}${domain}`,
|
||||||
name: domainToName(localize, domain, manifest),
|
name: domainToName(localize, domain, manifest),
|
||||||
description: "",
|
description: "",
|
||||||
@ -277,8 +335,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
return result.sort((a, b) =>
|
return result.sort((a, b) =>
|
||||||
stringCompare(a.name, b.name, this.hass.locale.language)
|
stringCompare(a.name, b.name, this.hass.locale.language)
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|
||||||
private _services = memoizeOne(
|
private _services = memoizeOne(
|
||||||
(
|
(
|
||||||
@ -302,9 +359,17 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
const services_keys = Object.keys(services[dmn]);
|
const services_keys = Object.keys(services[dmn]);
|
||||||
|
|
||||||
for (const service of services_keys) {
|
for (const service of services_keys) {
|
||||||
|
const icon = domainIconWithoutDefault(dmn);
|
||||||
result.push({
|
result.push({
|
||||||
group: false,
|
group: false,
|
||||||
icon: domainIcon(dmn),
|
icon,
|
||||||
|
image: !icon
|
||||||
|
? brandsUrl({
|
||||||
|
domain: dmn,
|
||||||
|
type: "icon",
|
||||||
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
|
})
|
||||||
|
: undefined,
|
||||||
key: `${SERVICE_PREFIX}${dmn}.${service}`,
|
key: `${SERVICE_PREFIX}${dmn}.${service}`,
|
||||||
name: `${domain ? "" : `${domainToName(localize, dmn)}: `}${
|
name: `${domain ? "" : `${domainToName(localize, dmn)}: `}${
|
||||||
this.hass.localize(`component.${dmn}.services.${service}.name`) ||
|
this.hass.localize(`component.${dmn}.services.${service}.name`) ||
|
||||||
@ -368,6 +433,19 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
this._height = boundingRect?.height;
|
this._height = boundingRect?.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected willUpdate(changedProperties: PropertyValues): void {
|
||||||
|
if (
|
||||||
|
this._params?.type === "action" &&
|
||||||
|
changedProperties.has("hass") &&
|
||||||
|
changedProperties.get("hass")?.states !== this.hass.states
|
||||||
|
) {
|
||||||
|
const domains = new Set(Object.keys(this.hass.states).map(computeDomain));
|
||||||
|
if (!deepEqual(domains, this._domains)) {
|
||||||
|
this._domains = domains;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this._params) {
|
if (!this._params) {
|
||||||
return nothing;
|
return nothing;
|
||||||
@ -376,6 +454,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
const items = this._filter
|
const items = this._filter
|
||||||
? this._getFilteredItems(
|
? this._getFilteredItems(
|
||||||
this._params.type,
|
this._params.type,
|
||||||
|
this._params.root,
|
||||||
this._group,
|
this._group,
|
||||||
this._filter,
|
this._filter,
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
@ -384,7 +463,9 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
)
|
)
|
||||||
: this._getGroupItems(
|
: this._getGroupItems(
|
||||||
this._params.type,
|
this._params.type,
|
||||||
|
this._params.root,
|
||||||
this._group,
|
this._group,
|
||||||
|
this._domains,
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
this.hass.services,
|
this.hass.services,
|
||||||
this._manifests
|
this._manifests
|
||||||
@ -451,7 +532,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
rootTabbable
|
rootTabbable
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
width: this._width ? `${this._width}px` : "auto",
|
width: this._width ? `${this._width}px` : "auto",
|
||||||
height: this._height ? `${Math.min(468, this._height)}px` : "auto",
|
height: this._height ? `${Math.min(670, this._height)}px` : "auto",
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
${this._params.clipboardItem &&
|
${this._params.clipboardItem &&
|
||||||
@ -497,7 +578,18 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
>
|
>
|
||||||
${item.name}
|
${item.name}
|
||||||
<span slot="secondary">${item.description}</span>
|
<span slot="secondary">${item.description}</span>
|
||||||
<ha-svg-icon slot="graphic" .path=${item.icon}></ha-svg-icon>
|
${item.icon
|
||||||
|
? html`<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${item.icon}
|
||||||
|
></ha-svg-icon>`
|
||||||
|
: html`<img
|
||||||
|
alt=""
|
||||||
|
slot="graphic"
|
||||||
|
src=${item.image}
|
||||||
|
crossorigin="anonymous"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
/>`}
|
||||||
${item.group
|
${item.group
|
||||||
? html`<ha-icon-next slot="meta"></ha-icon-next>`
|
? html`<ha-icon-next slot="meta"></ha-icon-next>`
|
||||||
: html`<ha-svg-icon
|
: html`<ha-svg-icon
|
||||||
@ -562,6 +654,10 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
ha-icon-next {
|
ha-icon-next {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
}
|
||||||
|
mwc-list {
|
||||||
|
max-height: 670px;
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
search-input {
|
search-input {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 16px;
|
margin: 0 16px;
|
||||||
|
@ -203,6 +203,7 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
showAddAutomationElementDialog(this, {
|
showAddAutomationElementDialog(this, {
|
||||||
type: "condition",
|
type: "condition",
|
||||||
add: this._addCondition,
|
add: this._addCondition,
|
||||||
|
root: !this.nested,
|
||||||
clipboardItem: this._clipboard?.condition?.condition,
|
clipboardItem: this._clipboard?.condition?.condition,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -168,6 +168,7 @@ export class HaDeviceCondition extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ha-form {
|
ha-form {
|
||||||
|
display: block;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -6,6 +6,7 @@ export interface AddAutomationElementDialogParams {
|
|||||||
type: "trigger" | "condition" | "action";
|
type: "trigger" | "condition" | "action";
|
||||||
add: (key: string) => void;
|
add: (key: string) => void;
|
||||||
clipboardItem: string | undefined;
|
clipboardItem: string | undefined;
|
||||||
|
root?: boolean;
|
||||||
group?: string;
|
group?: string;
|
||||||
}
|
}
|
||||||
const loadDialog = () => import("./add-automation-element-dialog");
|
const loadDialog = () => import("./add-automation-element-dialog");
|
||||||
|
@ -174,6 +174,7 @@ export class HaDeviceTrigger extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ha-form {
|
ha-form {
|
||||||
|
display: block;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -118,6 +118,8 @@ const OVERRIDE_DEVICE_CLASSES = {
|
|||||||
"carbon_monoxide",
|
"carbon_monoxide",
|
||||||
"moisture",
|
"moisture",
|
||||||
], // Alarm
|
], // Alarm
|
||||||
|
["connectivity"], // Connectivity
|
||||||
|
["update"], // Update
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,9 +101,9 @@ class DialogTodoItemEditor extends LitElement {
|
|||||||
scrimClickAction
|
scrimClickAction
|
||||||
.heading=${createCloseHeading(
|
.heading=${createCloseHeading(
|
||||||
this.hass,
|
this.hass,
|
||||||
isCreate
|
this.hass.localize(
|
||||||
? this.hass.localize("ui.components.todo.item.add")
|
`ui.components.todo.item.${isCreate ? "add" : "edit"}`
|
||||||
: this._summary
|
)
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@ -2492,13 +2492,13 @@
|
|||||||
"groups": {
|
"groups": {
|
||||||
"entity": {
|
"entity": {
|
||||||
"label": "Entity",
|
"label": "Entity",
|
||||||
"description": "When something happens to an entity"
|
"description": "When something happens to an entity."
|
||||||
},
|
},
|
||||||
"time_location": {
|
"time_location": {
|
||||||
"label": "Time and location",
|
"label": "Time and location",
|
||||||
"description": "When someone enters or leaves a zone, or at a specific time."
|
"description": "When someone enters or leaves a zone, or at a specific time."
|
||||||
},
|
},
|
||||||
"other": { "label": "Other" }
|
"other": { "label": "Other triggers" }
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"calendar": {
|
"calendar": {
|
||||||
@ -2534,7 +2534,7 @@
|
|||||||
"context_user_picked": "User firing event",
|
"context_user_picked": "User firing event",
|
||||||
"context_user_pick": "Select user",
|
"context_user_pick": "Select user",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "When an event is being received (event is an advanced concept in Home Assistant)",
|
"picker": "When an event is being received (event is an advanced concept in Home Assistant).",
|
||||||
"full": "When {eventTypes} event is fired"
|
"full": "When {eventTypes} event is fired"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2546,7 +2546,7 @@
|
|||||||
"enter": "Enter",
|
"enter": "Enter",
|
||||||
"leave": "Leave",
|
"leave": "Leave",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "When an entity created by a geolocation platform appears in or disappears from a zone",
|
"picker": "When an entity created by a geolocation platform appears in or disappears from a zone.",
|
||||||
"full": "When {source} {event, select, \n enter {enters}\n leave {leaves} other {} \n} {zone} {numberOfZones, plural,\n one {zone}\n other {zones}\n}"
|
"full": "When {source} {event, select, \n enter {enters}\n leave {leaves} other {} \n} {zone} {numberOfZones, plural,\n one {zone}\n other {zones}\n}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2608,7 +2608,7 @@
|
|||||||
"updated": "updated"
|
"updated": "updated"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "When a persistent notification is added or removed",
|
"picker": "When a persistent notification is added or removed.",
|
||||||
"full": "When a persistent notification is updated"
|
"full": "When a persistent notification is updated"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2619,7 +2619,7 @@
|
|||||||
"sunset": "Sunset",
|
"sunset": "Sunset",
|
||||||
"offset": "Offset (optional)",
|
"offset": "Offset (optional)",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "When the sun sets or rises",
|
"picker": "When the sun sets or rises.",
|
||||||
"sets": "When the sun sets{hasDuration, select, \n true { offset by {duration}} \n other {}\n }",
|
"sets": "When the sun sets{hasDuration, select, \n true { offset by {duration}} \n other {}\n }",
|
||||||
"rises": "When the sun rises{hasDuration, select, \n true { offset by {duration}} \n other {}\n }"
|
"rises": "When the sun rises{hasDuration, select, \n true { offset by {duration}} \n other {}\n }"
|
||||||
}
|
}
|
||||||
@ -2648,7 +2648,7 @@
|
|||||||
"value_template": "Value template",
|
"value_template": "Value template",
|
||||||
"for": "For",
|
"for": "For",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "When a template is evaluated to true.",
|
"picker": "When a template evaluates to true.",
|
||||||
"full": "When a template changes from false to true{hasDuration, select, \n true { for {duration}} \n other {}\n }"
|
"full": "When a template changes from false to true{hasDuration, select, \n true { for {duration}} \n other {}\n }"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2669,7 +2669,7 @@
|
|||||||
"minutes": "Minutes",
|
"minutes": "Minutes",
|
||||||
"seconds": "Seconds",
|
"seconds": "Seconds",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "Periodically, every defined interval of time."
|
"picker": "Periodically, at a defined interval."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webhook": {
|
"webhook": {
|
||||||
@ -2692,7 +2692,7 @@
|
|||||||
"enter": "Enter",
|
"enter": "Enter",
|
||||||
"leave": "Leave",
|
"leave": "Leave",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "When someone (or something) enters or leaves a zone",
|
"picker": "When someone (or something) enters or leaves a zone.",
|
||||||
"full": "When {entity} {event, select, \n enter {enters}\n leave {leaves} other {} \n} {zone} {numberOfZones, plural,\n one {zone} \n other {zones}\n}"
|
"full": "When {entity} {event, select, \n enter {enters}\n leave {leaves} other {} \n} {zone} {numberOfZones, plural,\n one {zone} \n other {zones}\n}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2734,7 +2734,7 @@
|
|||||||
"label": "Time and location",
|
"label": "Time and location",
|
||||||
"description": "If someone is in a zone or if the current time is before or after a specified time."
|
"description": "If someone is in a zone or if the current time is before or after a specified time."
|
||||||
},
|
},
|
||||||
"other": { "label": "Other" },
|
"other": { "label": "Other conditions" },
|
||||||
"building_blocks": {
|
"building_blocks": {
|
||||||
"label": "Building blocks",
|
"label": "Building blocks",
|
||||||
"description": "Build more complex conditions."
|
"description": "Build more complex conditions."
|
||||||
@ -2760,13 +2760,13 @@
|
|||||||
"preset_mode": "Preset mode"
|
"preset_mode": "Preset mode"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "If a device is in a certain state. Great way to start."
|
"picker": "Set of conditions provided by your device. Great way to start."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"not": {
|
"not": {
|
||||||
"label": "Not",
|
"label": "Not",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "Test if a condition is not true",
|
"picker": "Test if a condition is not true.",
|
||||||
"no_conditions": "Test if no condition matches",
|
"no_conditions": "Test if no condition matches",
|
||||||
"one_condition": "Test if 1 condition does not match",
|
"one_condition": "Test if 1 condition does not match",
|
||||||
"full": "Test if none of {count} conditions match"
|
"full": "Test if none of {count} conditions match"
|
||||||
@ -2800,7 +2800,7 @@
|
|||||||
"label": "[%key:ui::panel::config::automation::editor::triggers::type::state::label%]",
|
"label": "[%key:ui::panel::config::automation::editor::triggers::type::state::label%]",
|
||||||
"state": "[%key:ui::panel::config::automation::editor::triggers::type::state::label%]",
|
"state": "[%key:ui::panel::config::automation::editor::triggers::type::state::label%]",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "If an entity (or attribute) is in a specific state",
|
"picker": "If an entity (or attribute) is in a specific state.",
|
||||||
"no_entity": "Confirm state",
|
"no_entity": "Confirm state",
|
||||||
"full": "Confirm{hasAttribute, select, \n true { {attribute} of}\n other {}\n} {numberOfEntities, plural,\n zero {an entity is}\n one {{entities} is}\n other {{entities} are}\n} {numberOfStates, plural,\n zero {a state}\n other {{states}}\n}{hasDuration, select, \n true { for {duration}} \n other {}\n }"
|
"full": "Confirm{hasAttribute, select, \n true { {attribute} of}\n other {}\n} {numberOfEntities, plural,\n zero {an entity is}\n one {{entities} is}\n other {{entities} are}\n} {numberOfStates, plural,\n zero {a state}\n other {{states}}\n}{hasDuration, select, \n true { for {duration}} \n other {}\n }"
|
||||||
}
|
}
|
||||||
@ -2821,7 +2821,7 @@
|
|||||||
"label": "[%key:ui::panel::config::automation::editor::triggers::type::template::label%]",
|
"label": "[%key:ui::panel::config::automation::editor::triggers::type::template::label%]",
|
||||||
"value_template": "[%key:ui::panel::config::automation::editor::triggers::type::template::value_template%]",
|
"value_template": "[%key:ui::panel::config::automation::editor::triggers::type::template::value_template%]",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "If a template is evaluated to true.",
|
"picker": "If a template evaluates to true.",
|
||||||
"full": "Test if template renders a value equal to true"
|
"full": "Test if template renders a value equal to true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2844,7 +2844,7 @@
|
|||||||
"sun": "Sunday"
|
"sun": "Sunday"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "If the current time is before or after a specified time",
|
"picker": "If the current time is before or after a specified time.",
|
||||||
"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}"
|
"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}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2862,7 +2862,7 @@
|
|||||||
"entity": "[%key:ui::panel::config::automation::editor::triggers::type::zone::entity%]",
|
"entity": "[%key:ui::panel::config::automation::editor::triggers::type::zone::entity%]",
|
||||||
"zone": "[%key:ui::panel::config::automation::editor::triggers::type::zone::label%]",
|
"zone": "[%key:ui::panel::config::automation::editor::triggers::type::zone::label%]",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "If someone (or something) is in a zone",
|
"picker": "If someone (or something) is in a zone.",
|
||||||
"full": "Confirm {entity} {numberOfEntities, plural,\n one {is}\n other {are}\n} in {zone} {numberOfZones, plural,\n one {zone} \n other {zones}\n} "
|
"full": "Confirm {entity} {numberOfEntities, plural,\n one {is}\n other {are}\n} in {zone} {numberOfZones, plural,\n one {zone} \n other {zones}\n} "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2899,7 +2899,7 @@
|
|||||||
"continue_on_error": "Continue on error",
|
"continue_on_error": "Continue on error",
|
||||||
"groups": {
|
"groups": {
|
||||||
"helpers": { "label": "Helpers" },
|
"helpers": { "label": "Helpers" },
|
||||||
"other": { "label": "Other" },
|
"other": { "label": "Other actions" },
|
||||||
"building_blocks": {
|
"building_blocks": {
|
||||||
"label": "Building blocks",
|
"label": "Building blocks",
|
||||||
"description": "Build more complex sequences of actions."
|
"description": "Build more complex sequences of actions."
|
||||||
@ -2914,6 +2914,7 @@
|
|||||||
"description": {
|
"description": {
|
||||||
"service_based_on_template": "Call a service based on a template on {targets}",
|
"service_based_on_template": "Call a service based on a template on {targets}",
|
||||||
"service_based_on_name": "Call a service ''{name}'' on {targets}",
|
"service_based_on_name": "Call a service ''{name}'' on {targets}",
|
||||||
|
"service_name": "{domain} ''{name}'' on {targets}",
|
||||||
"service": "Call a service",
|
"service": "Call a service",
|
||||||
"target_template": "templated {name}",
|
"target_template": "templated {name}",
|
||||||
"target_unknown_entity": "unknown entity",
|
"target_unknown_entity": "unknown entity",
|
||||||
@ -2990,7 +2991,8 @@
|
|||||||
"flash": "Flash"
|
"flash": "Flash"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "Do something on a device. Great way to start."
|
"picker": "Do something on a device. Great way to start.",
|
||||||
|
"no_device": "Device action"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"activate_scene": {
|
"activate_scene": {
|
||||||
@ -3069,14 +3071,14 @@
|
|||||||
"response_variable": "The name of the variable to use as response",
|
"response_variable": "The name of the variable to use as response",
|
||||||
"error": "Stop because of an unexpected error",
|
"error": "Stop because of an unexpected error",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "Stop the sequence of actions",
|
"picker": "Stop the sequence of actions.",
|
||||||
"full": "Stop {hasReason, select, \n true { because: {reason}} \n other {}\n }"
|
"full": "Stop {hasReason, select, \n true { because: {reason}} \n other {}\n }"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"parallel": {
|
"parallel": {
|
||||||
"label": "Run in parallel",
|
"label": "Run in parallel",
|
||||||
"description": {
|
"description": {
|
||||||
"picker": "perform a sequence of actions in parallel.",
|
"picker": "Perform a sequence of actions in parallel.",
|
||||||
"full": "Run {number} {number, plural,\n one {action}\n other {actions}\n} in parallel"
|
"full": "Run {number} {number, plural,\n one {action}\n other {actions}\n} in parallel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user