mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-05 03:06:40 +00:00
Compare commits
32 Commits
fix-entity
...
20220902.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8ec2c38f72 | ||
![]() |
72aea57105 | ||
![]() |
031ecf5be8 | ||
![]() |
93e7927686 | ||
![]() |
320d8e6190 | ||
![]() |
efa4f65686 | ||
![]() |
ec257710ff | ||
![]() |
ffad6f340f | ||
![]() |
0b637fc9bd | ||
![]() |
9f9b0b6457 | ||
![]() |
a4227680de | ||
![]() |
5cfd263617 | ||
![]() |
430e671901 | ||
![]() |
8fcd396445 | ||
![]() |
e273b6b659 | ||
![]() |
2751adf440 | ||
![]() |
d661450121 | ||
![]() |
4511ded205 | ||
![]() |
2bf0c5d72d | ||
![]() |
604e5d5e09 | ||
![]() |
5466705d97 | ||
![]() |
774aee406c | ||
![]() |
775837b60f | ||
![]() |
030b2b921a | ||
![]() |
229bc26327 | ||
![]() |
99e85173eb | ||
![]() |
be0c22d7ae | ||
![]() |
fee1092a08 | ||
![]() |
d041bd9fd3 | ||
![]() |
df4b83349e | ||
![]() |
61b42249ec | ||
![]() |
40616b6af2 |
@@ -61,6 +61,7 @@ class HaDemo extends HomeAssistantAppEl {
|
|||||||
area_id: null,
|
area_id: null,
|
||||||
disabled_by: null,
|
disabled_by: null,
|
||||||
entity_id: "sensor.co2_intensity",
|
entity_id: "sensor.co2_intensity",
|
||||||
|
id: "sensor.co2_intensity",
|
||||||
name: null,
|
name: null,
|
||||||
icon: null,
|
icon: null,
|
||||||
platform: "co2signal",
|
platform: "co2signal",
|
||||||
@@ -74,6 +75,7 @@ class HaDemo extends HomeAssistantAppEl {
|
|||||||
area_id: null,
|
area_id: null,
|
||||||
disabled_by: null,
|
disabled_by: null,
|
||||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||||
|
id: "sensor.co2_intensity",
|
||||||
name: null,
|
name: null,
|
||||||
icon: null,
|
icon: null,
|
||||||
platform: "co2signal",
|
platform: "co2signal",
|
||||||
|
@@ -3,7 +3,7 @@ title: Dialgos
|
|||||||
subtitle: Dialogs provide important prompts in a user flow.
|
subtitle: Dialogs provide important prompts in a user flow.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Material Desing 3
|
# Material Design 3
|
||||||
|
|
||||||
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on it's [website](https://m3.material.io/components/dialogs/overview).
|
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on it's [website](https://m3.material.io/components/dialogs/overview).
|
||||||
|
|
||||||
|
@@ -191,6 +191,7 @@ const createEntityRegistryEntries = (
|
|||||||
hidden_by: null,
|
hidden_by: null,
|
||||||
entity_category: null,
|
entity_category: null,
|
||||||
entity_id: "binary_sensor.updater",
|
entity_id: "binary_sensor.updater",
|
||||||
|
id: "binary_sensor.updater",
|
||||||
name: null,
|
name: null,
|
||||||
icon: null,
|
icon: null,
|
||||||
platform: "updater",
|
platform: "updater",
|
||||||
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20220816.0"
|
version = "20220902.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"
|
||||||
|
@@ -126,6 +126,7 @@ export const FIXED_DEVICE_CLASS_ICONS = {
|
|||||||
gas: mdiGasCylinder,
|
gas: mdiGasCylinder,
|
||||||
humidity: mdiWaterPercent,
|
humidity: mdiWaterPercent,
|
||||||
illuminance: mdiBrightness5,
|
illuminance: mdiBrightness5,
|
||||||
|
moisture: mdiWaterPercent,
|
||||||
monetary: mdiCash,
|
monetary: mdiCash,
|
||||||
nitrogen_dioxide: mdiMolecule,
|
nitrogen_dioxide: mdiMolecule,
|
||||||
nitrogen_monoxide: mdiMolecule,
|
nitrogen_monoxide: mdiMolecule,
|
||||||
|
28
src/common/datetime/format_duration.ts
Normal file
28
src/common/datetime/format_duration.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { HaDurationData } from "../../components/ha-duration-input";
|
||||||
|
|
||||||
|
const leftPad = (num: number) => (num < 10 ? `0${num}` : num);
|
||||||
|
|
||||||
|
export const formatDuration = (duration: HaDurationData) => {
|
||||||
|
const d = duration.days || 0;
|
||||||
|
const h = duration.hours || 0;
|
||||||
|
const m = duration.minutes || 0;
|
||||||
|
const s = duration.seconds || 0;
|
||||||
|
const ms = duration.milliseconds || 0;
|
||||||
|
|
||||||
|
if (d > 0) {
|
||||||
|
return `${d} days ${h}:${leftPad(m)}:${leftPad(s)}`;
|
||||||
|
}
|
||||||
|
if (h > 0) {
|
||||||
|
return `${h}:${leftPad(m)}:${leftPad(s)}`;
|
||||||
|
}
|
||||||
|
if (m > 0) {
|
||||||
|
return `${m}:${leftPad(s)}`;
|
||||||
|
}
|
||||||
|
if (s > 0) {
|
||||||
|
return `${s} seconds`;
|
||||||
|
}
|
||||||
|
if (ms > 0) {
|
||||||
|
return `${ms} milliseconds`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
@@ -3,6 +3,8 @@ import { mdiDotsVertical } from "@mdi/js";
|
|||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import { css, html, LitElement, TemplateResult } from "lit";
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import "./ha-button-menu";
|
import "./ha-button-menu";
|
||||||
import "./ha-icon-button";
|
import "./ha-icon-button";
|
||||||
@@ -16,6 +18,7 @@ export interface IconOverflowMenuItem {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
onClick: CallableFunction;
|
onClick: CallableFunction;
|
||||||
|
warning?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-icon-overflow-menu")
|
@customElement("ha-icon-overflow-menu")
|
||||||
@@ -49,9 +52,13 @@ export class HaIconOverflowMenu extends LitElement {
|
|||||||
graphic="icon"
|
graphic="icon"
|
||||||
.disabled=${item.disabled}
|
.disabled=${item.disabled}
|
||||||
@click=${item.action}
|
@click=${item.action}
|
||||||
|
class=${classMap({ warning: Boolean(item.warning) })}
|
||||||
>
|
>
|
||||||
<div slot="graphic">
|
<div slot="graphic">
|
||||||
<ha-svg-icon .path=${item.path}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
|
class=${classMap({ warning: Boolean(item.warning) })}
|
||||||
|
.path=${item.path}
|
||||||
|
></ha-svg-icon>
|
||||||
</div>
|
</div>
|
||||||
${item.label}
|
${item.label}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
@@ -81,7 +88,8 @@ export class HaIconOverflowMenu extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _handleIconOverflowMenuOpened() {
|
protected _handleIconOverflowMenuOpened(e) {
|
||||||
|
e.stopPropagation();
|
||||||
// If this component is used inside a data table, the z-index of the row
|
// If this component is used inside a data table, the z-index of the row
|
||||||
// needs to be increased. Otherwise the ha-button-menu would be displayed
|
// needs to be increased. Otherwise the ha-button-menu would be displayed
|
||||||
// underneath the next row in the table.
|
// underneath the next row in the table.
|
||||||
@@ -99,12 +107,15 @@ export class HaIconOverflowMenu extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [
|
||||||
:host {
|
haStyle,
|
||||||
display: flex;
|
css`
|
||||||
justify-content: flex-end;
|
:host {
|
||||||
}
|
display: flex;
|
||||||
`;
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,14 @@
|
|||||||
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
||||||
|
import { ensureArray } from "../common/ensure-array";
|
||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import { Condition, Trigger } from "./automation";
|
import { Condition, Trigger } from "./automation";
|
||||||
|
import {
|
||||||
|
DeviceCondition,
|
||||||
|
DeviceTrigger,
|
||||||
|
localizeDeviceAutomationCondition,
|
||||||
|
localizeDeviceAutomationTrigger,
|
||||||
|
} from "./device_automation";
|
||||||
import { formatAttributeName } from "./entity_attributes";
|
import { formatAttributeName } from "./entity_attributes";
|
||||||
|
|
||||||
export const describeTrigger = (
|
export const describeTrigger = (
|
||||||
@@ -68,7 +75,7 @@ export const describeTrigger = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// State Trigger
|
// State Trigger
|
||||||
if (trigger.platform === "state" && trigger.entity_id) {
|
if (trigger.platform === "state") {
|
||||||
let base = "When";
|
let base = "When";
|
||||||
let entities = "";
|
let entities = "";
|
||||||
|
|
||||||
@@ -89,12 +96,17 @@ export const describeTrigger = (
|
|||||||
} ${computeStateName(states[entity]) || entity}`;
|
} ${computeStateName(states[entity]) || entity}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (trigger.entity_id) {
|
||||||
entities = states[trigger.entity_id]
|
entities = states[trigger.entity_id]
|
||||||
? computeStateName(states[trigger.entity_id])
|
? computeStateName(states[trigger.entity_id])
|
||||||
: trigger.entity_id;
|
: trigger.entity_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!entities) {
|
||||||
|
// no entity_id or empty array
|
||||||
|
entities = "something";
|
||||||
|
}
|
||||||
|
|
||||||
base += ` ${entities} changes`;
|
base += ` ${entities} changes`;
|
||||||
|
|
||||||
if (trigger.from) {
|
if (trigger.from) {
|
||||||
@@ -280,7 +292,7 @@ export const describeTrigger = (
|
|||||||
}
|
}
|
||||||
// MQTT Trigger
|
// MQTT Trigger
|
||||||
if (trigger.platform === "mqtt") {
|
if (trigger.platform === "mqtt") {
|
||||||
return "When a MQTT payload has been received";
|
return "When an MQTT message has been received";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template Trigger
|
// Template Trigger
|
||||||
@@ -292,7 +304,25 @@ export const describeTrigger = (
|
|||||||
if (trigger.platform === "webhook") {
|
if (trigger.platform === "webhook") {
|
||||||
return "When a Webhook payload has been received";
|
return "When a Webhook payload has been received";
|
||||||
}
|
}
|
||||||
return `${trigger.platform || "Unknown"} trigger`;
|
|
||||||
|
if (trigger.platform === "device") {
|
||||||
|
if (!trigger.device_id) {
|
||||||
|
return "Device trigger";
|
||||||
|
}
|
||||||
|
const config = trigger as DeviceTrigger;
|
||||||
|
const localized = localizeDeviceAutomationTrigger(hass, config);
|
||||||
|
if (localized) {
|
||||||
|
return localized;
|
||||||
|
}
|
||||||
|
const stateObj = hass.states[config.entity_id as string];
|
||||||
|
return `${stateObj ? computeStateName(stateObj) : config.entity_id} ${
|
||||||
|
config.type
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${
|
||||||
|
trigger.platform ? trigger.platform.replace(/_/g, " ") : "Unknown"
|
||||||
|
} trigger`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const describeCondition = (
|
export const describeCondition = (
|
||||||
@@ -304,15 +334,64 @@ export const describeCondition = (
|
|||||||
return condition.alias;
|
return condition.alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (["or", "and", "not"].includes(condition.condition)) {
|
if (!condition.condition) {
|
||||||
return `multiple conditions using "${condition.condition}"`;
|
const shorthands: Array<"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],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition.condition === "or") {
|
||||||
|
const conditions = ensureArray(condition.conditions);
|
||||||
|
|
||||||
|
let count = "condition";
|
||||||
|
|
||||||
|
if (conditions && conditions.length > 0) {
|
||||||
|
count = `of ${conditions.length} conditions`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Test if any ${count} matches`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition.condition === "and") {
|
||||||
|
const conditions = ensureArray(condition.conditions);
|
||||||
|
|
||||||
|
const count =
|
||||||
|
conditions && conditions.length > 0
|
||||||
|
? `${conditions.length} `
|
||||||
|
: "multiple";
|
||||||
|
|
||||||
|
return `Test if ${count} conditions match`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition.condition === "not") {
|
||||||
|
const conditions = ensureArray(condition.conditions);
|
||||||
|
|
||||||
|
const what =
|
||||||
|
conditions && conditions.length > 0
|
||||||
|
? `none of ${conditions.length} conditions match`
|
||||||
|
: "no condition matches";
|
||||||
|
|
||||||
|
return `Test if ${what}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// State Condition
|
// State Condition
|
||||||
if (condition.condition === "state" && condition.entity_id) {
|
if (condition.condition === "state") {
|
||||||
let base = "Confirm";
|
let base = "Confirm";
|
||||||
const stateObj = hass.states[condition.entity_id];
|
const stateObj = hass.states[condition.entity_id];
|
||||||
const entity = stateObj ? computeStateName(stateObj) : condition.entity_id;
|
const entity = stateObj
|
||||||
|
? computeStateName(stateObj)
|
||||||
|
: condition.entity_id
|
||||||
|
? condition.entity_id
|
||||||
|
: "an entity";
|
||||||
|
|
||||||
if ("attribute" in condition) {
|
if ("attribute" in condition) {
|
||||||
base += ` ${condition.attribute} from`;
|
base += ` ${condition.attribute} from`;
|
||||||
@@ -328,10 +407,14 @@ export const describeCondition = (
|
|||||||
: ""
|
: ""
|
||||||
} ${state}`;
|
} ${state}`;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (condition.state) {
|
||||||
states = condition.state.toString();
|
states = condition.state.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!states) {
|
||||||
|
states = "a state";
|
||||||
|
}
|
||||||
|
|
||||||
base += ` ${entity} is ${states}`;
|
base += ` ${entity} is ${states}`;
|
||||||
|
|
||||||
if ("for" in condition) {
|
if ("for" in condition) {
|
||||||
@@ -467,5 +550,22 @@ export const describeCondition = (
|
|||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${condition.condition} condition`;
|
if (condition.condition === "device") {
|
||||||
|
if (!condition.device_id) {
|
||||||
|
return "Device condition";
|
||||||
|
}
|
||||||
|
const config = condition as DeviceCondition;
|
||||||
|
const localized = localizeDeviceAutomationCondition(hass, config);
|
||||||
|
if (localized) {
|
||||||
|
return localized;
|
||||||
|
}
|
||||||
|
const stateObj = hass.states[config.entity_id as string];
|
||||||
|
return `${stateObj ? computeStateName(stateObj) : config.entity_id} ${
|
||||||
|
config.type
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${
|
||||||
|
condition.condition ? condition.condition.replace(/_/g, " ") : "Unknown"
|
||||||
|
} condition`;
|
||||||
};
|
};
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
import { Connection, createCollection } from "home-assistant-js-websocket";
|
import { Connection, createCollection } from "home-assistant-js-websocket";
|
||||||
import { Store } from "home-assistant-js-websocket/dist/store";
|
import { Store } from "home-assistant-js-websocket/dist/store";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||||
import { debounce } from "../common/util/debounce";
|
import { debounce } from "../common/util/debounce";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
export interface EntityRegistryEntry {
|
export interface EntityRegistryEntry {
|
||||||
|
id: string;
|
||||||
entity_id: string;
|
entity_id: string;
|
||||||
name: string | null;
|
name: string | null;
|
||||||
icon: string | null;
|
icon: string | null;
|
||||||
@@ -161,6 +163,16 @@ export const sortEntityRegistryByName = (entries: EntityRegistryEntry[]) =>
|
|||||||
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "")
|
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const entityRegistryById = memoizeOne(
|
||||||
|
(entries: HomeAssistant["entities"]) => {
|
||||||
|
const entities: HomeAssistant["entities"] = {};
|
||||||
|
for (const entity of Object.values(entries)) {
|
||||||
|
entities[entity.id] = entity;
|
||||||
|
}
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const getEntityPlatformLookup = (
|
export const getEntityPlatformLookup = (
|
||||||
entities: EntityRegistryEntry[]
|
entities: EntityRegistryEntry[]
|
||||||
): Record<string, string> => {
|
): Record<string, string> => {
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { formatDuration } from "../common/datetime/format_duration";
|
||||||
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
||||||
import { ensureArray } from "../common/ensure-array";
|
import { ensureArray } from "../common/ensure-array";
|
||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
@@ -5,6 +6,13 @@ import { isTemplate } from "../common/string/has-template";
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { Condition } from "./automation";
|
import { Condition } from "./automation";
|
||||||
import { describeCondition, describeTrigger } from "./automation_i18n";
|
import { describeCondition, describeTrigger } from "./automation_i18n";
|
||||||
|
import { localizeDeviceAutomationAction } from "./device_automation";
|
||||||
|
import { computeDeviceName } from "./device_registry";
|
||||||
|
import {
|
||||||
|
computeEntityRegistryName,
|
||||||
|
entityRegistryById,
|
||||||
|
} from "./entity_registry";
|
||||||
|
import { domainToName } from "./integration";
|
||||||
import {
|
import {
|
||||||
ActionType,
|
ActionType,
|
||||||
ActionTypes,
|
ActionTypes,
|
||||||
@@ -47,9 +55,13 @@ export const describeAction = <T extends ActionType>(
|
|||||||
) {
|
) {
|
||||||
base = "Call a service based on a template";
|
base = "Call a service based on a template";
|
||||||
} else if (config.service) {
|
} else if (config.service) {
|
||||||
base = `Call service ${config.service}`;
|
const [domain, serviceName] = config.service.split(".", 2);
|
||||||
|
const service = hass.services[domain][serviceName];
|
||||||
|
base = service
|
||||||
|
? `${domainToName(hass.localize, domain)}: ${service.name}`
|
||||||
|
: `Call service: ${config.service}`;
|
||||||
} else {
|
} else {
|
||||||
return actionType;
|
return "Call a service";
|
||||||
}
|
}
|
||||||
if (config.target) {
|
if (config.target) {
|
||||||
const targets: string[] = [];
|
const targets: string[] = [];
|
||||||
@@ -66,26 +78,49 @@ export const describeAction = <T extends ActionType>(
|
|||||||
? config.target[key]
|
? config.target[key]
|
||||||
: [config.target[key]];
|
: [config.target[key]];
|
||||||
|
|
||||||
const values: string[] = [];
|
|
||||||
|
|
||||||
let renderValues = true;
|
|
||||||
|
|
||||||
for (const targetThing of keyConf) {
|
for (const targetThing of keyConf) {
|
||||||
if (isTemplate(targetThing)) {
|
if (isTemplate(targetThing)) {
|
||||||
targets.push(`templated ${label}`);
|
targets.push(`templated ${label}`);
|
||||||
renderValues = false;
|
|
||||||
break;
|
break;
|
||||||
|
} else if (key === "entity_id") {
|
||||||
|
if (targetThing.includes(".")) {
|
||||||
|
const state = hass.states[targetThing];
|
||||||
|
if (state) {
|
||||||
|
targets.push(computeStateName(state));
|
||||||
|
} else {
|
||||||
|
targets.push(targetThing);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const entityReg = entityRegistryById(hass.entities)[targetThing];
|
||||||
|
if (entityReg) {
|
||||||
|
targets.push(
|
||||||
|
computeEntityRegistryName(hass, entityReg) || targetThing
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
targets.push("unknown entity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (key === "device_id") {
|
||||||
|
const device = hass.devices[targetThing];
|
||||||
|
if (device) {
|
||||||
|
targets.push(computeDeviceName(device, hass));
|
||||||
|
} else {
|
||||||
|
targets.push("unknown device");
|
||||||
|
}
|
||||||
|
} else if (key === "area_id") {
|
||||||
|
const area = hass.areas[targetThing];
|
||||||
|
if (area?.name) {
|
||||||
|
targets.push(area.name);
|
||||||
|
} else {
|
||||||
|
targets.push("unknown area");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
values.push(targetThing);
|
targets.push(targetThing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renderValues) {
|
|
||||||
targets.push(`${label} ${values.join(", ")}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (targets.length > 0) {
|
if (targets.length > 0) {
|
||||||
base += ` on ${targets.join(", ")}`;
|
base += ` ${targets.join(", ")}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,9 +137,11 @@ export const describeAction = <T extends ActionType>(
|
|||||||
} else if (typeof config.delay === "string") {
|
} else if (typeof config.delay === "string") {
|
||||||
duration = isTemplate(config.delay)
|
duration = isTemplate(config.delay)
|
||||||
? "based on a template"
|
? "based on a template"
|
||||||
: `for ${config.delay}`;
|
: `for ${config.delay || "a duration"}`;
|
||||||
|
} else if (config.delay) {
|
||||||
|
duration = `for ${formatDuration(config.delay)}`;
|
||||||
} else {
|
} else {
|
||||||
duration = `for ${JSON.stringify(config.delay)}`;
|
duration = "for a duration";
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Delay ${duration}`;
|
return `Delay ${duration}`;
|
||||||
@@ -118,13 +155,12 @@ export const describeAction = <T extends ActionType>(
|
|||||||
} else {
|
} else {
|
||||||
entityId = config.target?.entity_id || config.entity_id;
|
entityId = config.target?.entity_id || config.entity_id;
|
||||||
}
|
}
|
||||||
|
if (!entityId) {
|
||||||
|
return "Activate a scene";
|
||||||
|
}
|
||||||
const sceneStateObj = entityId ? hass.states[entityId] : undefined;
|
const sceneStateObj = entityId ? hass.states[entityId] : undefined;
|
||||||
return `Scene ${
|
return `Active scene ${
|
||||||
sceneStateObj
|
sceneStateObj ? computeStateName(sceneStateObj) : entityId
|
||||||
? computeStateName(sceneStateObj)
|
|
||||||
: "scene" in config
|
|
||||||
? config.scene
|
|
||||||
: config.target?.entity_id || config.entity_id || ""
|
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,16 +168,22 @@ export const describeAction = <T extends ActionType>(
|
|||||||
const config = action as PlayMediaAction;
|
const config = action as PlayMediaAction;
|
||||||
const entityId = config.target?.entity_id || config.entity_id;
|
const entityId = config.target?.entity_id || config.entity_id;
|
||||||
const mediaStateObj = entityId ? hass.states[entityId] : undefined;
|
const mediaStateObj = entityId ? hass.states[entityId] : undefined;
|
||||||
return `Play ${config.metadata.title || config.data.media_content_id} on ${
|
return `Play ${
|
||||||
|
config.metadata.title || config.data.media_content_id || "media"
|
||||||
|
} on ${
|
||||||
mediaStateObj
|
mediaStateObj
|
||||||
? computeStateName(mediaStateObj)
|
? computeStateName(mediaStateObj)
|
||||||
: config.target?.entity_id || config.entity_id
|
: entityId || "a media player"
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionType === "wait_for_trigger") {
|
if (actionType === "wait_for_trigger") {
|
||||||
const config = action as WaitForTriggerAction;
|
const config = action as WaitForTriggerAction;
|
||||||
return `Wait for ${ensureArray(config.wait_for_trigger)
|
const triggers = ensureArray(config.wait_for_trigger);
|
||||||
|
if (!triggers || triggers.length === 0) {
|
||||||
|
return "Wait for a trigger";
|
||||||
|
}
|
||||||
|
return `Wait for ${triggers
|
||||||
.map((trigger) => describeTrigger(trigger, hass))
|
.map((trigger) => describeTrigger(trigger, hass))
|
||||||
.join(", ")}`;
|
.join(", ")}`;
|
||||||
}
|
}
|
||||||
@@ -164,22 +206,26 @@ export const describeAction = <T extends ActionType>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (actionType === "check_condition") {
|
if (actionType === "check_condition") {
|
||||||
return `Test ${describeCondition(action as Condition, hass)}`;
|
return describeCondition(action as Condition, hass);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionType === "stop") {
|
if (actionType === "stop") {
|
||||||
const config = action as StopAction;
|
const config = action as StopAction;
|
||||||
return `Stopped${config.stop ? ` because: ${config.stop}` : ""}`;
|
return `Stop${config.stop ? ` because: ${config.stop}` : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionType === "if") {
|
if (actionType === "if") {
|
||||||
const config = action as IfAction;
|
const config = action as IfAction;
|
||||||
return `Perform an action if: ${
|
return `Perform an action if: ${
|
||||||
typeof config.if === "string"
|
!config.if
|
||||||
|
? ""
|
||||||
|
: typeof config.if === "string"
|
||||||
? config.if
|
? config.if
|
||||||
: ensureArray(config.if).length > 1
|
: ensureArray(config.if).length > 1
|
||||||
? `${ensureArray(config.if).length} conditions`
|
? `${ensureArray(config.if).length} conditions`
|
||||||
: describeCondition(ensureArray(config.if)[0], hass)
|
: ensureArray(config.if).length
|
||||||
|
? describeCondition(ensureArray(config.if)[0], hass)
|
||||||
|
: ""
|
||||||
}${config.else ? " (or else!)" : ""}`;
|
}${config.else ? " (or else!)" : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,6 +265,13 @@ export const describeAction = <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) {
|
||||||
|
return "Device action";
|
||||||
|
}
|
||||||
|
const localized = localizeDeviceAutomationAction(hass, config);
|
||||||
|
if (localized) {
|
||||||
|
return localized;
|
||||||
|
}
|
||||||
const stateObj = hass.states[config.entity_id as string];
|
const stateObj = hass.states[config.entity_id as string];
|
||||||
return `${config.type || "Perform action with"} ${
|
return `${config.type || "Perform action with"} ${
|
||||||
stateObj ? computeStateName(stateObj) : config.entity_id
|
stateObj ? computeStateName(stateObj) : config.entity_id
|
||||||
|
@@ -15,6 +15,8 @@ class HassSubpage extends LitElement {
|
|||||||
|
|
||||||
@property({ type: String, attribute: "back-path" }) public backPath?: string;
|
@property({ type: String, attribute: "back-path" }) public backPath?: string;
|
||||||
|
|
||||||
|
@property() public backCallback?: () => void;
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public supervisor = false;
|
@property({ type: Boolean }) public supervisor = false;
|
||||||
@@ -52,6 +54,9 @@ class HassSubpage extends LitElement {
|
|||||||
<slot name="toolbar-icon"></slot>
|
<slot name="toolbar-icon"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="content" @scroll=${this._saveScrollPos}><slot></slot></div>
|
<div class="content" @scroll=${this._saveScrollPos}><slot></slot></div>
|
||||||
|
<div id="fab">
|
||||||
|
<slot name="fab"></slot>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +66,10 @@ class HassSubpage extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _backTapped(): void {
|
private _backTapped(): void {
|
||||||
|
if (this.backCallback) {
|
||||||
|
this.backCallback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
history.back();
|
history.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +125,29 @@ class HassSubpage extends LitElement {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fab {
|
||||||
|
position: fixed;
|
||||||
|
right: calc(16px + env(safe-area-inset-right));
|
||||||
|
bottom: calc(16px + env(safe-area-inset-bottom));
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
:host([narrow]) #fab.tabs {
|
||||||
|
bottom: calc(84px + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
#fab[is-wide] {
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
}
|
||||||
|
:host([rtl]) #fab {
|
||||||
|
right: auto;
|
||||||
|
left: calc(16px + env(safe-area-inset-left));
|
||||||
|
}
|
||||||
|
:host([rtl][is-wide]) #fab {
|
||||||
|
bottom: 24px;
|
||||||
|
left: 24px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -157,13 +157,13 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
</div>`
|
</div>`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-expansion-panel leftChevron>
|
<ha-expansion-panel leftChevron>
|
||||||
<div slot="header">
|
<h3 slot="header">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="action-icon"
|
class="action-icon"
|
||||||
.path=${ACTION_TYPES[type!]}
|
.path=${ACTION_TYPES[type!]}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${capitalizeFirstLetter(describeAction(this.hass, this.action))}
|
${capitalizeFirstLetter(describeAction(this.hass, this.action))}
|
||||||
</div>
|
</h3>
|
||||||
|
|
||||||
${this.index !== 0
|
${this.index !== 0
|
||||||
? html`
|
? html`
|
||||||
@@ -230,6 +230,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
|
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
|
||||||
${!yamlMode
|
${!yamlMode
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
class="selected_menu_item"
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${mdiCheck}
|
.path=${mdiCheck}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
@@ -242,6 +243,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
)}
|
)}
|
||||||
${yamlMode
|
${yamlMode
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
class="selected_menu_item"
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${mdiCheck}
|
.path=${mdiCheck}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
@@ -507,13 +509,18 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
--expansion-panel-summary-padding: 0 0 0 8px;
|
--expansion-panel-summary-padding: 0 0 0 8px;
|
||||||
--expansion-panel-content-padding: 0;
|
--expansion-panel-content-padding: 0;
|
||||||
}
|
}
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
.action-icon {
|
.action-icon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@media (min-width: 870px) {
|
@media (min-width: 870px) {
|
||||||
.action-icon {
|
.action-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: var(--primary-color);
|
color: var(--secondary-text-color);
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
@@ -534,6 +541,12 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
.warning ul {
|
.warning ul {
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
}
|
}
|
||||||
|
.selected_menu_item {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
li[role="separator"] {
|
||||||
|
border-bottom-color: var(--divider-color);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,8 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import "../../../components/entity/ha-entity-toggle";
|
|
||||||
import "../../../components/ha-blueprint-picker";
|
import "../../../components/ha-blueprint-picker";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-circular-progress";
|
import "../../../components/ha-circular-progress";
|
||||||
@@ -11,10 +10,7 @@ import "../../../components/ha-markdown";
|
|||||||
import "../../../components/ha-selector/ha-selector";
|
import "../../../components/ha-selector/ha-selector";
|
||||||
import "../../../components/ha-settings-row";
|
import "../../../components/ha-settings-row";
|
||||||
import "../../../components/ha-textfield";
|
import "../../../components/ha-textfield";
|
||||||
import {
|
import { BlueprintAutomationConfig } from "../../../data/automation";
|
||||||
BlueprintAutomationConfig,
|
|
||||||
triggerAutomationActions,
|
|
||||||
} from "../../../data/automation";
|
|
||||||
import {
|
import {
|
||||||
BlueprintOrError,
|
BlueprintOrError,
|
||||||
Blueprints,
|
Blueprints,
|
||||||
@@ -38,8 +34,6 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
|
|
||||||
@state() private _blueprints?: Blueprints;
|
@state() private _blueprints?: Blueprints;
|
||||||
|
|
||||||
@state() private _showDescription = false;
|
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this._getBlueprints();
|
this._getBlueprints();
|
||||||
@@ -52,88 +46,28 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
return this._blueprints[this.config.use_blueprint.path];
|
return this._blueprints[this.config.use_blueprint.path];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected willUpdate(changedProps: PropertyValues): void {
|
|
||||||
super.willUpdate(changedProps);
|
|
||||||
if (
|
|
||||||
!this._showDescription &&
|
|
||||||
changedProps.has("config") &&
|
|
||||||
this.config.description
|
|
||||||
) {
|
|
||||||
this._showDescription = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const blueprint = this._blueprint;
|
const blueprint = this._blueprint;
|
||||||
return html`
|
return html`
|
||||||
<ha-config-section vertical .isWide=${this.isWide}>
|
<p class="introduction">
|
||||||
${!this.narrow
|
${this.hass.localize("ui.panel.config.automation.editor.introduction")}
|
||||||
? html` <span slot="header">${this.config.alias}</span> `
|
</p>
|
||||||
: ""}
|
<ha-card outlined>
|
||||||
<span slot="introduction">
|
<div class="card-content">
|
||||||
${this.hass.localize(
|
<ha-textarea
|
||||||
"ui.panel.config.automation.editor.introduction"
|
.label=${this.hass.localize(
|
||||||
)}
|
"ui.panel.config.automation.editor.description.label"
|
||||||
</span>
|
)}
|
||||||
<ha-card outlined>
|
.placeholder=${this.hass.localize(
|
||||||
<div class="card-content">
|
"ui.panel.config.automation.editor.description.placeholder"
|
||||||
${this._showDescription
|
)}
|
||||||
? html`
|
name="description"
|
||||||
<ha-textarea
|
autogrow
|
||||||
.label=${this.hass.localize(
|
.value=${this.config.description || ""}
|
||||||
"ui.panel.config.automation.editor.description.label"
|
@change=${this._valueChanged}
|
||||||
)}
|
></ha-textarea>
|
||||||
.placeholder=${this.hass.localize(
|
</div>
|
||||||
"ui.panel.config.automation.editor.description.placeholder"
|
</ha-card>
|
||||||
)}
|
|
||||||
name="description"
|
|
||||||
autogrow
|
|
||||||
.value=${this.config.description || ""}
|
|
||||||
@change=${this._valueChanged}
|
|
||||||
></ha-textarea>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<div class="link-button-row">
|
|
||||||
<button class="link" @click=${this._addDescription}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.description.add"
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`}
|
|
||||||
</div>
|
|
||||||
${this.stateObj
|
|
||||||
? html`
|
|
||||||
<div class="card-actions layout horizontal justified center">
|
|
||||||
<div class="layout horizontal center">
|
|
||||||
<ha-entity-toggle
|
|
||||||
.hass=${this.hass}
|
|
||||||
.stateObj=${this.stateObj!}
|
|
||||||
></ha-entity-toggle>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.enable_disable"
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a href="/config/automation/trace/${this.config.id}">
|
|
||||||
<mwc-button>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.show_trace"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
</a>
|
|
||||||
<mwc-button
|
|
||||||
@click=${this._runActions}
|
|
||||||
.stateObj=${this.stateObj}
|
|
||||||
>
|
|
||||||
${this.hass.localize("ui.card.automation.trigger")}
|
|
||||||
</mwc-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</ha-card>
|
|
||||||
</ha-config-section>
|
|
||||||
|
|
||||||
<ha-card
|
<ha-card
|
||||||
outlined
|
outlined
|
||||||
@@ -220,10 +154,6 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
this._blueprints = await fetchBlueprints(this.hass, "automation");
|
this._blueprints = await fetchBlueprints(this.hass, "automation");
|
||||||
}
|
}
|
||||||
|
|
||||||
private _runActions(ev: Event) {
|
|
||||||
triggerAutomationActions(this.hass, (ev.target as any).stateObj.entity_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _blueprintChanged(ev) {
|
private _blueprintChanged(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
if (this.config.use_blueprint.path === ev.detail.value) {
|
if (this.config.use_blueprint.path === ev.detail.value) {
|
||||||
@@ -284,16 +214,14 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addDescription() {
|
|
||||||
this._showDescription = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
ha-card.blueprint {
|
ha-card.blueprint {
|
||||||
max-width: 1040px;
|
|
||||||
margin: 24px auto;
|
margin: 24px auto;
|
||||||
}
|
}
|
||||||
.padding {
|
.padding {
|
||||||
@@ -313,15 +241,16 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
h3 {
|
h3 {
|
||||||
margin: 16px;
|
margin: 16px;
|
||||||
}
|
}
|
||||||
span[slot="introduction"] a {
|
.introduction {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.introduction a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
ha-entity-toggle {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
ha-settings-row {
|
ha-settings-row {
|
||||||
--paper-time-input-justify-content: flex-end;
|
--paper-time-input-justify-content: flex-end;
|
||||||
--settings-row-content-width: 100%;
|
--settings-row-content-width: 100%;
|
||||||
|
@@ -93,7 +93,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-expansion-panel leftChevron>
|
<ha-expansion-panel leftChevron>
|
||||||
<div slot="header">
|
<h3 slot="header">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="condition-icon"
|
class="condition-icon"
|
||||||
.path=${CONDITION_TYPES[this.condition.condition]}
|
.path=${CONDITION_TYPES[this.condition.condition]}
|
||||||
@@ -101,7 +101,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
${capitalizeFirstLetter(
|
${capitalizeFirstLetter(
|
||||||
describeCondition(this.condition, this.hass)
|
describeCondition(this.condition, this.hass)
|
||||||
)}
|
)}
|
||||||
</div>
|
</h3>
|
||||||
|
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
slot="icons"
|
slot="icons"
|
||||||
@@ -145,6 +145,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
|
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
|
||||||
${!this._yamlMode
|
${!this._yamlMode
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
class="selected_menu_item"
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${mdiCheck}
|
.path=${mdiCheck}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
@@ -157,6 +158,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
)}
|
)}
|
||||||
${this._yamlMode
|
${this._yamlMode
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
class="selected_menu_item"
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${mdiCheck}
|
.path=${mdiCheck}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
@@ -423,13 +425,18 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
--expansion-panel-summary-padding: 0 0 0 8px;
|
--expansion-panel-summary-padding: 0 0 0 8px;
|
||||||
--expansion-panel-content-padding: 0;
|
--expansion-panel-content-padding: 0;
|
||||||
}
|
}
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
.condition-icon {
|
.condition-icon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@media (min-width: 870px) {
|
@media (min-width: 870px) {
|
||||||
.condition-icon {
|
.condition-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: var(--primary-color);
|
color: var(--secondary-text-color);
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
@@ -472,6 +479,12 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
.testing.pass {
|
.testing.pass {
|
||||||
background-color: var(--success-color);
|
background-color: var(--success-color);
|
||||||
}
|
}
|
||||||
|
.selected_menu_item {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
li[role="separator"] {
|
||||||
|
border-bottom-color: var(--divider-color);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import {
|
|||||||
mdiContentSave,
|
mdiContentSave,
|
||||||
mdiDelete,
|
mdiDelete,
|
||||||
mdiDotsVertical,
|
mdiDotsVertical,
|
||||||
|
mdiInformationOutline,
|
||||||
mdiPencil,
|
mdiPencil,
|
||||||
mdiPlay,
|
mdiPlay,
|
||||||
mdiPlayCircleOutline,
|
mdiPlayCircleOutline,
|
||||||
@@ -25,6 +26,7 @@ import {
|
|||||||
} from "lit";
|
} from "lit";
|
||||||
import { property, state, query } from "lit/decorators";
|
import { property, state, query } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||||
import "../../../components/ha-button-menu";
|
import "../../../components/ha-button-menu";
|
||||||
@@ -49,7 +51,7 @@ import {
|
|||||||
showPromptDialog,
|
showPromptDialog,
|
||||||
} from "../../../dialogs/generic/show-dialog-box";
|
} from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/ha-app-layout";
|
import "../../../layouts/ha-app-layout";
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
import "../../../layouts/hass-subpage";
|
||||||
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
@@ -111,7 +113,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
? this.hass.states[this._entityId]
|
? this.hass.states[this._entityId]
|
||||||
: undefined;
|
: undefined;
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
@@ -125,6 +127,14 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
|
||||||
|
<mwc-list-item graphic="icon" @click=${this._showInfo}>
|
||||||
|
${this.hass.localize("ui.panel.config.automation.editor.show_info")}
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiInformationOutline}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-list-item>
|
||||||
|
|
||||||
<mwc-list-item
|
<mwc-list-item
|
||||||
graphic="icon"
|
graphic="icon"
|
||||||
.disabled=${!stateObj}
|
.disabled=${!stateObj}
|
||||||
@@ -134,15 +144,9 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
|
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
|
|
||||||
${stateObj
|
${stateObj && this._config
|
||||||
? html`<a
|
? html`<a href="/config/automation/trace/${this._config.id}">
|
||||||
href="/config/automation/trace/${this._config
|
<mwc-list-item graphic="icon">
|
||||||
? this._config.id
|
|
||||||
: ""}"
|
|
||||||
target="_blank"
|
|
||||||
.disabled=${!stateObj}
|
|
||||||
>
|
|
||||||
<mwc-list-item graphic="icon" .disabled=${!stateObj}>
|
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.show_trace"
|
"ui.panel.config.automation.editor.show_trace"
|
||||||
)}
|
)}
|
||||||
@@ -230,6 +234,14 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
|
|
||||||
${this._config
|
${this._config
|
||||||
? html`
|
? html`
|
||||||
|
${this.narrow
|
||||||
|
? html`<span slot="header"
|
||||||
|
>${this._config!.alias ||
|
||||||
|
this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.default_name"
|
||||||
|
)}</span
|
||||||
|
>`
|
||||||
|
: ""}
|
||||||
<div
|
<div
|
||||||
class="content ${classMap({
|
class="content ${classMap({
|
||||||
"yaml-mode": this._mode === "yaml",
|
"yaml-mode": this._mode === "yaml",
|
||||||
@@ -242,12 +254,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
${this._mode === "gui"
|
${this._mode === "gui"
|
||||||
? html`
|
? html`
|
||||||
${this.narrow
|
${this.narrow
|
||||||
? html`<span slot="header"
|
? ""
|
||||||
>${this._config!.alias ||
|
|
||||||
this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.default_name"
|
|
||||||
)}</span
|
|
||||||
>`
|
|
||||||
: html`
|
: html`
|
||||||
<div class="header-name">
|
<div class="header-name">
|
||||||
<h1>
|
<h1>
|
||||||
@@ -329,7 +336,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||||
</ha-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage>
|
</hass-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,6 +437,13 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
this._errors = undefined;
|
this._errors = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _showInfo() {
|
||||||
|
if (!this.hass || !this._entityId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fireEvent(this, "hass-more-info", { entityId: this._entityId });
|
||||||
|
}
|
||||||
|
|
||||||
private _runActions() {
|
private _runActions() {
|
||||||
if (!this.hass || !this._entityId) {
|
if (!this.hass || !this._entityId) {
|
||||||
return;
|
return;
|
||||||
@@ -637,7 +651,8 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
manual-automation-editor {
|
manual-automation-editor,
|
||||||
|
blueprint-automation-editor {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 1040px;
|
max-width: 1040px;
|
||||||
padding: 28px 20px 0;
|
padding: 28px 20px 0;
|
||||||
@@ -678,15 +693,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: var(--paper-font-headline_-_font-family);
|
|
||||||
-webkit-font-smoothing: var(
|
|
||||||
--paper-font-headline_-_-webkit-font-smoothing
|
|
||||||
);
|
|
||||||
font-size: var(--paper-font-headline_-_font-size);
|
|
||||||
font-weight: var(--paper-font-headline_-_font-weight);
|
|
||||||
letter-spacing: var(--paper-font-headline_-_letter-spacing);
|
|
||||||
line-height: var(--paper-font-headline_-_line-height);
|
|
||||||
opacity: var(--dark-primary-opacity);
|
|
||||||
}
|
}
|
||||||
.header-name {
|
.header-name {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@@ -1,4 +1,16 @@
|
|||||||
import { mdiHelpCircle, mdiInformationOutline, mdiPlus } from "@mdi/js";
|
import {
|
||||||
|
mdiCancel,
|
||||||
|
mdiContentDuplicate,
|
||||||
|
mdiDelete,
|
||||||
|
mdiHelpCircle,
|
||||||
|
mdiInformationOutline,
|
||||||
|
mdiPlay,
|
||||||
|
mdiPlayCircleOutline,
|
||||||
|
mdiPlus,
|
||||||
|
mdiStopCircleOutline,
|
||||||
|
mdiTransitConnection,
|
||||||
|
} from "@mdi/js";
|
||||||
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -13,11 +25,22 @@ import type {
|
|||||||
RowClickedEvent,
|
RowClickedEvent,
|
||||||
} from "../../../components/data-table/ha-data-table";
|
} from "../../../components/data-table/ha-data-table";
|
||||||
import "../../../components/ha-button-related-filter-menu";
|
import "../../../components/ha-button-related-filter-menu";
|
||||||
|
import "../../../components/ha-chip";
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-fab";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-icon-overflow-menu";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import type { AutomationEntity } from "../../../data/automation";
|
import {
|
||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
AutomationEntity,
|
||||||
|
deleteAutomation,
|
||||||
|
getAutomationConfig,
|
||||||
|
showAutomationEditor,
|
||||||
|
triggerAutomationActions,
|
||||||
|
} from "../../../data/automation";
|
||||||
|
import {
|
||||||
|
showAlertDialog,
|
||||||
|
showConfirmationDialog,
|
||||||
|
} from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
@@ -63,6 +86,7 @@ class HaAutomationPicker extends LitElement {
|
|||||||
...automation,
|
...automation,
|
||||||
name: computeStateName(automation),
|
name: computeStateName(automation),
|
||||||
last_triggered: automation.attributes.last_triggered || undefined,
|
last_triggered: automation.attributes.last_triggered || undefined,
|
||||||
|
disabled: automation.state === "off",
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -123,22 +147,105 @@ class HaAutomationPicker extends LitElement {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
columns.disabled = this.narrow
|
||||||
|
? {
|
||||||
|
title: "",
|
||||||
|
template: (disabled: boolean) =>
|
||||||
|
disabled
|
||||||
|
? html`
|
||||||
|
<paper-tooltip animation-delay="0" position="left">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.picker.disabled"
|
||||||
|
)}
|
||||||
|
</paper-tooltip>
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${mdiCancel}
|
||||||
|
style="color: var(--secondary-text-color)"
|
||||||
|
></ha-svg-icon>
|
||||||
|
`
|
||||||
|
: "",
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
width: "20%",
|
||||||
|
title: "",
|
||||||
|
template: (disabled: boolean) =>
|
||||||
|
disabled
|
||||||
|
? html`
|
||||||
|
<ha-chip>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.picker.disabled"
|
||||||
|
)}
|
||||||
|
</ha-chip>
|
||||||
|
`
|
||||||
|
: "",
|
||||||
|
};
|
||||||
|
|
||||||
columns.actions = {
|
columns.actions = {
|
||||||
title: "",
|
title: "",
|
||||||
label: this.hass.localize(
|
width: this.narrow ? undefined : "10%",
|
||||||
"ui.panel.config.automation.picker.headers.actions"
|
type: "overflow-menu",
|
||||||
),
|
template: (_: string, automation: any) =>
|
||||||
type: "icon-button",
|
html`
|
||||||
template: (_info, automation: any) => html`
|
<ha-icon-overflow-menu
|
||||||
<ha-icon-button
|
.hass=${this.hass}
|
||||||
.automation=${automation}
|
narrow
|
||||||
.label=${this.hass.localize(
|
.items=${[
|
||||||
"ui.panel.config.automation.picker.headers.actions"
|
{
|
||||||
)}
|
path: mdiInformationOutline,
|
||||||
.path=${mdiInformationOutline}
|
label: this.hass.localize(
|
||||||
@click=${this._showInfo}
|
"ui.panel.config.automation.editor.show_info"
|
||||||
></ha-icon-button>
|
),
|
||||||
`,
|
action: () => this._showInfo(automation),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: mdiPlay,
|
||||||
|
label: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.run"
|
||||||
|
),
|
||||||
|
action: () => this._runActions(automation),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: mdiTransitConnection,
|
||||||
|
label: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.show_trace"
|
||||||
|
),
|
||||||
|
action: () => this._showTrace(automation),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: mdiContentDuplicate,
|
||||||
|
label: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.picker.duplicate"
|
||||||
|
),
|
||||||
|
action: () => this.duplicate(automation),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:
|
||||||
|
automation.state === "off"
|
||||||
|
? mdiPlayCircleOutline
|
||||||
|
: mdiStopCircleOutline,
|
||||||
|
label:
|
||||||
|
automation.state === "off"
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.enable"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.disable"
|
||||||
|
),
|
||||||
|
action: () => this._toggle(automation),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.picker.delete"
|
||||||
|
),
|
||||||
|
path: mdiDelete,
|
||||||
|
action: () => this._deleteConfirm(automation),
|
||||||
|
warning: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
</ha-icon-overflow-menu>
|
||||||
|
`,
|
||||||
};
|
};
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
@@ -210,12 +317,52 @@ class HaAutomationPicker extends LitElement {
|
|||||||
this._filterValue = undefined;
|
this._filterValue = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showInfo(ev) {
|
private _showInfo(automation: any) {
|
||||||
ev.stopPropagation();
|
|
||||||
const automation = ev.currentTarget.automation;
|
|
||||||
fireEvent(this, "hass-more-info", { entityId: automation.entity_id });
|
fireEvent(this, "hass-more-info", { entityId: automation.entity_id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _runActions(automation: any) {
|
||||||
|
triggerAutomationActions(this.hass, automation.entity_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _showTrace(automation: any) {
|
||||||
|
navigate(`/config/automation/trace/${automation.attributes.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _toggle(automation): Promise<void> {
|
||||||
|
const service = automation.state === "off" ? "turn_on" : "turn_off";
|
||||||
|
await this.hass.callService("automation", service, {
|
||||||
|
entity_id: automation.entity_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _deleteConfirm(automation) {
|
||||||
|
showConfirmationDialog(this, {
|
||||||
|
text: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.picker.delete_confirm"
|
||||||
|
),
|
||||||
|
confirmText: this.hass!.localize("ui.common.delete"),
|
||||||
|
dismissText: this.hass!.localize("ui.common.cancel"),
|
||||||
|
confirm: () => this._delete(automation),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _delete(automation) {
|
||||||
|
await deleteAutomation(this.hass, automation.attributes.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async duplicate(automation) {
|
||||||
|
const config = await getAutomationConfig(
|
||||||
|
this.hass,
|
||||||
|
automation.attributes.id
|
||||||
|
);
|
||||||
|
showAutomationEditor({
|
||||||
|
...config,
|
||||||
|
id: undefined,
|
||||||
|
alias: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _showHelp() {
|
private _showHelp() {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.hass.localize("ui.panel.config.automation.caption"),
|
title: this.hass.localize("ui.panel.config.automation.caption"),
|
||||||
|
@@ -47,12 +47,12 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-expansion-panel leftChevron>
|
<ha-expansion-panel leftChevron>
|
||||||
<div slot="header">
|
<h3 slot="header">
|
||||||
<ha-svg-icon class="settings-icon" .path=${mdiRobot}></ha-svg-icon>
|
<ha-svg-icon class="settings-icon" .path=${mdiRobot}></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.automation_settings"
|
"ui.panel.config.automation.editor.automation_settings"
|
||||||
)}
|
)}
|
||||||
</div>
|
</h3>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ha-textarea
|
<ha-textarea
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@@ -115,11 +115,11 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
</ha-card>
|
</ha-card>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="name">
|
<h2 id="triggers-heading" class="name">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.header"
|
"ui.panel.config.automation.editor.triggers.header"
|
||||||
)}
|
)}
|
||||||
</div>
|
</h2>
|
||||||
<a
|
<a
|
||||||
href=${documentationUrl(this.hass, "/docs/automation/trigger/")}
|
href=${documentationUrl(this.hass, "/docs/automation/trigger/")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -135,17 +135,19 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ha-automation-trigger
|
<ha-automation-trigger
|
||||||
|
role="region"
|
||||||
|
aria-labelledby="triggers-heading"
|
||||||
.triggers=${this.config.trigger}
|
.triggers=${this.config.trigger}
|
||||||
@value-changed=${this._triggerChanged}
|
@value-changed=${this._triggerChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
></ha-automation-trigger>
|
></ha-automation-trigger>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="name">
|
<h2 id="conditions-heading" class="name">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.conditions.header"
|
"ui.panel.config.automation.editor.conditions.header"
|
||||||
)}
|
)}
|
||||||
</div>
|
</h2>
|
||||||
<a
|
<a
|
||||||
href=${documentationUrl(this.hass, "/docs/automation/condition/")}
|
href=${documentationUrl(this.hass, "/docs/automation/condition/")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -161,17 +163,19 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ha-automation-condition
|
<ha-automation-condition
|
||||||
|
role="region"
|
||||||
|
aria-labelledby="conditions-heading"
|
||||||
.conditions=${this.config.condition || []}
|
.conditions=${this.config.condition || []}
|
||||||
@value-changed=${this._conditionChanged}
|
@value-changed=${this._conditionChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="name">
|
<h2 id="actions-heading" class="name">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.header"
|
"ui.panel.config.automation.editor.actions.header"
|
||||||
)}
|
)}
|
||||||
</div>
|
</h2>
|
||||||
<a
|
<a
|
||||||
href=${documentationUrl(this.hass, "/docs/automation/action/")}
|
href=${documentationUrl(this.hass, "/docs/automation/action/")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -187,6 +191,8 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
|
role="region"
|
||||||
|
aria-labelledby="actions-heading"
|
||||||
.actions=${this.config.action}
|
.actions=${this.config.action}
|
||||||
@value-changed=${this._actionChanged}
|
@value-changed=${this._actionChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -292,7 +298,6 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 16px 0;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.header .name {
|
.header .name {
|
||||||
@@ -303,6 +308,11 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
.header a {
|
.header a {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
ha-expansion-panel {
|
ha-expansion-panel {
|
||||||
--expansion-panel-summary-padding: 0 0 0 8px;
|
--expansion-panel-summary-padding: 0 0 0 8px;
|
||||||
--expansion-panel-content-padding: 0;
|
--expansion-panel-content-padding: 0;
|
||||||
@@ -319,7 +329,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
@media (min-width: 870px) {
|
@media (min-width: 870px) {
|
||||||
.settings-icon {
|
.settings-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: var(--primary-color);
|
color: var(--secondary-text-color);
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
@@ -121,13 +121,13 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-expansion-panel leftChevron>
|
<ha-expansion-panel leftChevron>
|
||||||
<div slot="header">
|
<h3 slot="header">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="trigger-icon"
|
class="trigger-icon"
|
||||||
.path=${TRIGGER_TYPES[this.trigger.platform]}
|
.path=${TRIGGER_TYPES[this.trigger.platform]}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${capitalizeFirstLetter(describeTrigger(this.trigger, this.hass))}
|
${capitalizeFirstLetter(describeTrigger(this.trigger, this.hass))}
|
||||||
</div>
|
</h3>
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
slot="icons"
|
slot="icons"
|
||||||
fixed
|
fixed
|
||||||
@@ -170,6 +170,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
|
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
|
||||||
${!yamlMode
|
${!yamlMode
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
class="selected_menu_item"
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${mdiCheck}
|
.path=${mdiCheck}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
@@ -182,6 +183,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
)}
|
)}
|
||||||
${yamlMode
|
${yamlMode
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
|
class="selected_menu_item"
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${mdiCheck}
|
.path=${mdiCheck}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
@@ -532,13 +534,18 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
--expansion-panel-summary-padding: 0 0 0 8px;
|
--expansion-panel-summary-padding: 0 0 0 8px;
|
||||||
--expansion-panel-content-padding: 0;
|
--expansion-panel-content-padding: 0;
|
||||||
}
|
}
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
.trigger-icon {
|
.trigger-icon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@media (min-width: 870px) {
|
@media (min-width: 870px) {
|
||||||
.trigger-icon {
|
.trigger-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: var(--primary-color);
|
color: var(--secondary-text-color);
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
@@ -587,6 +594,12 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
.selected_menu_item {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
li[role="separator"] {
|
||||||
|
border-bottom-color: var(--divider-color);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
|
|||||||
|
|
||||||
@property() public trigger!: TagTrigger;
|
@property() public trigger!: TagTrigger;
|
||||||
|
|
||||||
@state() private _tags: Tag[] = [];
|
@state() private _tags?: Tag[];
|
||||||
|
|
||||||
public static get defaultConfig() {
|
public static get defaultConfig() {
|
||||||
return { tag_id: "" };
|
return { tag_id: "" };
|
||||||
@@ -27,14 +27,16 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const { tag_id } = this.trigger;
|
if (!this._tags) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
return html`
|
return html`
|
||||||
<ha-select
|
<ha-select
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.type.tag.label"
|
"ui.panel.config.automation.editor.triggers.type.tag.label"
|
||||||
)}
|
)}
|
||||||
.disabled=${this._tags.length === 0}
|
.disabled=${this._tags.length === 0}
|
||||||
.value=${tag_id}
|
.value=${this.trigger.tag_id}
|
||||||
@selected=${this._tagChanged}
|
@selected=${this._tagChanged}
|
||||||
>
|
>
|
||||||
${this._tags.map(
|
${this._tags.map(
|
||||||
@@ -49,13 +51,19 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _fetchTags() {
|
private async _fetchTags() {
|
||||||
this._tags = await fetchTags(this.hass);
|
this._tags = (await fetchTags(this.hass)).sort((a, b) =>
|
||||||
this._tags.sort((a, b) =>
|
|
||||||
caseInsensitiveStringCompare(a.name || a.id, b.name || b.id)
|
caseInsensitiveStringCompare(a.name || a.id, b.name || b.id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _tagChanged(ev) {
|
private _tagChanged(ev) {
|
||||||
|
if (
|
||||||
|
!ev.detail.value ||
|
||||||
|
!this._tags ||
|
||||||
|
this.trigger.tag_id === ev.detail.value
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: {
|
value: {
|
||||||
...this.trigger,
|
...this.trigger,
|
||||||
|
@@ -944,7 +944,18 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
action: async () => {
|
action: async () => {
|
||||||
const confirmed = await showConfirmationDialog(this, {
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
text: this.hass.localize("ui.panel.config.devices.confirm_delete"),
|
text:
|
||||||
|
this._integrations(device, this.entries).length > 1
|
||||||
|
? this.hass.localize(
|
||||||
|
`ui.panel.config.devices.confirm_delete_integration`,
|
||||||
|
{
|
||||||
|
integration: domainToName(
|
||||||
|
this.hass.localize,
|
||||||
|
entry.domain
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: this.hass.localize(`ui.panel.config.devices.confirm_delete`),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
@@ -960,7 +971,7 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
classes: "warning",
|
classes: "warning",
|
||||||
icon: mdiDelete,
|
icon: mdiDelete,
|
||||||
label:
|
label:
|
||||||
buttons.length > 1
|
this._integrations(device, this.entries).length > 1
|
||||||
? this.hass.localize(
|
? this.hass.localize(
|
||||||
`ui.panel.config.devices.delete_device_integration`,
|
`ui.panel.config.devices.delete_device_integration`,
|
||||||
{
|
{
|
||||||
|
@@ -872,10 +872,17 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
|||||||
name: this._name.trim() || null,
|
name: this._name.trim() || null,
|
||||||
icon: this._icon.trim() || null,
|
icon: this._icon.trim() || null,
|
||||||
area_id: this._areaId || null,
|
area_id: this._areaId || null,
|
||||||
device_class: this._deviceClass || null,
|
|
||||||
new_entity_id: this._entityId.trim(),
|
new_entity_id: this._entityId.trim(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only update device class if changed by user
|
||||||
|
if (
|
||||||
|
this._deviceClass !==
|
||||||
|
(this.entry.device_class || this.entry.original_device_class)
|
||||||
|
) {
|
||||||
|
params.device_class = this._deviceClass;
|
||||||
|
}
|
||||||
|
|
||||||
const stateObj: HassEntity | undefined =
|
const stateObj: HassEntity | undefined =
|
||||||
this.hass.states[this.entry.entity_id];
|
this.hass.states[this.entry.entity_id];
|
||||||
const domain = computeDomain(this.entry.entity_id);
|
const domain = computeDomain(this.entry.entity_id);
|
||||||
@@ -1051,9 +1058,10 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
|||||||
.buttons {
|
.buttons {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 8px 24px 24px 24px;
|
padding: 24px;
|
||||||
|
padding-top: 16px;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-bottom: max(env(safe-area-inset-bottom), 16px);
|
padding-bottom: max(env(safe-area-inset-bottom), 24px);
|
||||||
background-color: var(--mdc-theme-surface, #fff);
|
background-color: var(--mdc-theme-surface, #fff);
|
||||||
border-top: 1px solid var(--divider-color);
|
border-top: 1px solid var(--divider-color);
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
@@ -68,9 +68,10 @@ import type { HomeAssistant, Route } from "../../../types";
|
|||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import "../integrations/ha-integration-overflow-menu";
|
import "../integrations/ha-integration-overflow-menu";
|
||||||
|
|
||||||
export interface StateEntity extends EntityRegistryEntry {
|
export interface StateEntity extends Omit<EntityRegistryEntry, "id"> {
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
selectable?: boolean;
|
selectable?: boolean;
|
||||||
|
id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EntityRow extends StateEntity {
|
export interface EntityRow extends StateEntity {
|
||||||
@@ -302,7 +303,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
private _filteredEntitiesAndDomains = memoize(
|
private _filteredEntitiesAndDomains = memoize(
|
||||||
(
|
(
|
||||||
entities: EntityRegistryEntry[],
|
entities: StateEntity[],
|
||||||
devices: DeviceRegistryEntry[] | undefined,
|
devices: DeviceRegistryEntry[] | undefined,
|
||||||
areas: AreaRegistryEntry[] | undefined,
|
areas: AreaRegistryEntry[] | undefined,
|
||||||
stateEntities: StateEntity[],
|
stateEntities: StateEntity[],
|
||||||
@@ -392,7 +393,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
result.push({
|
result.push({
|
||||||
...entry,
|
...entry,
|
||||||
entity,
|
entity,
|
||||||
name: computeEntityRegistryName(this.hass!, entry),
|
name: computeEntityRegistryName(
|
||||||
|
this.hass!,
|
||||||
|
entry as EntityRegistryEntry
|
||||||
|
),
|
||||||
unavailable,
|
unavailable,
|
||||||
restored,
|
restored,
|
||||||
area: area ? area.name : "—",
|
area: area ? area.name : "—",
|
||||||
|
@@ -36,7 +36,6 @@ const defaultFullCalendarConfig: CalendarOptions = {
|
|||||||
selectOverlap: false,
|
selectOverlap: false,
|
||||||
eventOverlap: false,
|
eventOverlap: false,
|
||||||
allDaySlot: false,
|
allDaySlot: false,
|
||||||
slotMinTime: "00:00:59",
|
|
||||||
height: "parent",
|
height: "parent",
|
||||||
locales: allLocales,
|
locales: allLocales,
|
||||||
firstDay: 1,
|
firstDay: 1,
|
||||||
@@ -178,7 +177,7 @@ class HaScheduleForm extends LitElement {
|
|||||||
},
|
},
|
||||||
eventTimeFormat: {
|
eventTimeFormat: {
|
||||||
hour: useAmPm(this.hass.locale) ? "numeric" : "2-digit",
|
hour: useAmPm(this.hass.locale) ? "numeric" : "2-digit",
|
||||||
minute: undefined,
|
minute: useAmPm(this.hass.locale) ? "numeric" : "2-digit",
|
||||||
hour12: useAmPm(this.hass.locale),
|
hour12: useAmPm(this.hass.locale),
|
||||||
meridiem: useAmPm(this.hass.locale) ? "narrow" : false,
|
meridiem: useAmPm(this.hass.locale) ? "narrow" : false,
|
||||||
},
|
},
|
||||||
@@ -214,7 +213,8 @@ class HaScheduleForm extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this[`_${day}`].forEach((item: ScheduleDay, index: number) => {
|
this[`_${day}`].forEach((item: ScheduleDay, index: number) => {
|
||||||
const distance = i - currentDay;
|
// Add 7 to 0 because we start the calendar on Monday
|
||||||
|
const distance = i - currentDay + (i === 0 ? 7 : 0);
|
||||||
|
|
||||||
const start = new Date();
|
const start = new Date();
|
||||||
start.setDate(start.getDate() + distance);
|
start.setDate(start.getDate() + distance);
|
||||||
@@ -227,7 +227,9 @@ class HaScheduleForm extends LitElement {
|
|||||||
end.setDate(end.getDate() + distance);
|
end.setDate(end.getDate() + distance);
|
||||||
end.setHours(
|
end.setHours(
|
||||||
parseInt(item.to.slice(0, 2)),
|
parseInt(item.to.slice(0, 2)),
|
||||||
parseInt(item.to.slice(-2))
|
parseInt(item.to.slice(-2)),
|
||||||
|
0,
|
||||||
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
events.push({
|
events.push({
|
||||||
@@ -381,6 +383,9 @@ class HaScheduleForm extends LitElement {
|
|||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
height: 450px;
|
height: 450px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
.fc-scroller {
|
.fc-scroller {
|
||||||
overflow-x: visible !important;
|
overflow-x: visible !important;
|
||||||
|
@@ -210,7 +210,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
"ui.panel.config.automation.editor.edit_yaml"
|
"ui.panel.config.automation.editor.edit_yaml"
|
||||||
)}
|
)}
|
||||||
graphic="icon"
|
graphic="icon"
|
||||||
?activated=${this._mode === "yaml"}
|
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
|
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
|
||||||
${this._mode === "yaml"
|
${this._mode === "yaml"
|
||||||
@@ -331,11 +330,11 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="name">
|
<h2 id="sequence-heading" class="name">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.script.editor.sequence"
|
"ui.panel.config.script.editor.sequence"
|
||||||
)}
|
)}
|
||||||
</div>
|
</h2>
|
||||||
<a
|
<a
|
||||||
href=${documentationUrl(
|
href=${documentationUrl(
|
||||||
this.hass,
|
this.hass,
|
||||||
@@ -354,6 +353,8 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
|
role="region"
|
||||||
|
aria-labelledby="sequence-heading"
|
||||||
.actions=${this._config.sequence}
|
.actions=${this._config.sequence}
|
||||||
@value-changed=${this._sequenceChanged}
|
@value-changed=${this._sequenceChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -831,6 +832,9 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
.header a {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -1793,6 +1793,7 @@
|
|||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"delete_confirm": "Are you sure you want to delete this automation?",
|
"delete_confirm": "Are you sure you want to delete this automation?",
|
||||||
"duplicate": "Duplicate",
|
"duplicate": "Duplicate",
|
||||||
|
"disabled": "Disabled",
|
||||||
"headers": {
|
"headers": {
|
||||||
"toggle": "Enable/disable",
|
"toggle": "Enable/disable",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
@@ -1822,6 +1823,7 @@
|
|||||||
"run": "[%key:ui::panel::config::automation::editor::actions::run%]",
|
"run": "[%key:ui::panel::config::automation::editor::actions::run%]",
|
||||||
"rename": "[%key:ui::panel::config::automation::editor::triggers::rename%]",
|
"rename": "[%key:ui::panel::config::automation::editor::triggers::rename%]",
|
||||||
"show_trace": "Traces",
|
"show_trace": "Traces",
|
||||||
|
"show_info": "Information",
|
||||||
"introduction": "Use automations to bring your home to life.",
|
"introduction": "Use automations to bring your home to life.",
|
||||||
"default_name": "New Automation",
|
"default_name": "New Automation",
|
||||||
"missing_name": "Cannot save automation without a name",
|
"missing_name": "Cannot save automation without a name",
|
||||||
@@ -2576,7 +2578,7 @@
|
|||||||
"download_diagnostics": "Download diagnostics",
|
"download_diagnostics": "Download diagnostics",
|
||||||
"download_diagnostics_integration": "Download {integration} diagnostics",
|
"download_diagnostics_integration": "Download {integration} diagnostics",
|
||||||
"delete_device": "Delete",
|
"delete_device": "Delete",
|
||||||
"delete_device_integration": "Remove {integration} from device",
|
"delete_device_integration": "Remove device from {integration}",
|
||||||
"type": {
|
"type": {
|
||||||
"device_heading": "Device",
|
"device_heading": "Device",
|
||||||
"device": "device",
|
"device": "device",
|
||||||
@@ -2653,6 +2655,7 @@
|
|||||||
},
|
},
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"confirm_delete": "Are you sure you want to delete this device?",
|
"confirm_delete": "Are you sure you want to delete this device?",
|
||||||
|
"confirm_delete_integration": "Are you sure you want to remove this device from {integration}?",
|
||||||
"picker": {
|
"picker": {
|
||||||
"search": "Search devices",
|
"search": "Search devices",
|
||||||
"filter": {
|
"filter": {
|
||||||
|
Reference in New Issue
Block a user