mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-20 02:38:53 +00:00
Automation editor show targets within rows (#28510)
* Automation editor show targets within rows * review * Fix expandable row icons * Use state icon instead of state-badge * Fix target wrap * Use default font weight for automation rows * Remove comma from targets in row
This commit is contained in:
@@ -142,7 +142,7 @@ export class DemoAutomationDescribeAction extends LitElement {
|
|||||||
<div class="action">
|
<div class="action">
|
||||||
<span>
|
<span>
|
||||||
${this._action
|
${this._action
|
||||||
? describeAction(this.hass, [], [], {}, this._action)
|
? describeAction(this.hass, [], this._action)
|
||||||
: "<invalid YAML>"}
|
: "<invalid YAML>"}
|
||||||
</span>
|
</span>
|
||||||
<ha-yaml-editor
|
<ha-yaml-editor
|
||||||
@@ -155,7 +155,7 @@ export class DemoAutomationDescribeAction extends LitElement {
|
|||||||
${ACTIONS.map(
|
${ACTIONS.map(
|
||||||
(conf) => html`
|
(conf) => html`
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<span>${describeAction(this.hass, [], [], {}, conf as any)}</span>
|
<span>${describeAction(this.hass, [], conf as any)}</span>
|
||||||
<pre>${dump(conf)}</pre>
|
<pre>${dump(conf)}</pre>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ export class HaAutomationRow extends LitElement {
|
|||||||
<slot name="leading-icon"></slot>
|
<slot name="leading-icon"></slot>
|
||||||
</div>
|
</div>
|
||||||
<slot class="header" name="header"></slot>
|
<slot class="header" name="header"></slot>
|
||||||
<slot name="icons"></slot>
|
<div class="icons">
|
||||||
|
<slot name="icons"></slot>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -118,12 +120,11 @@ export class HaAutomationRow extends LitElement {
|
|||||||
}
|
}
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: var(--ha-space-0) var(--ha-space-2);
|
padding: 0 var(--ha-space-3);
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-weight: var(--ha-font-weight-medium);
|
|
||||||
outline: none;
|
outline: none;
|
||||||
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
||||||
}
|
}
|
||||||
@@ -140,11 +141,15 @@ export class HaAutomationRow extends LitElement {
|
|||||||
background-color: var(--ha-color-fill-neutral-loud-resting);
|
background-color: var(--ha-color-fill-neutral-loud-resting);
|
||||||
border-radius: var(--ha-border-radius-md);
|
border-radius: var(--ha-border-radius-md);
|
||||||
padding: var(--ha-space-1);
|
padding: var(--ha-space-1);
|
||||||
|
margin-top: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
}
|
}
|
||||||
|
.leading-icon-wrapper {
|
||||||
|
padding-top: var(--ha-space-3);
|
||||||
|
}
|
||||||
::slotted([slot="leading-icon"]) {
|
::slotted([slot="leading-icon"]) {
|
||||||
color: var(--ha-color-on-neutral-quiet);
|
color: var(--ha-color-on-neutral-quiet);
|
||||||
}
|
}
|
||||||
@@ -172,6 +177,10 @@ export class HaAutomationRow extends LitElement {
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
margin: var(--ha-space-0) var(--ha-space-3);
|
margin: var(--ha-space-0) var(--ha-space-3);
|
||||||
}
|
}
|
||||||
|
.icons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
:host([sort-selected]) .row {
|
:host([sort-selected]) .row {
|
||||||
outline: solid;
|
outline: solid;
|
||||||
outline-color: rgba(var(--rgb-accent-color), 0.6);
|
outline-color: rgba(var(--rgb-accent-color), 0.6);
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export class HaDomainIcon extends LitElement {
|
|||||||
if (icn) {
|
if (icn) {
|
||||||
return html`<ha-icon .icon=${icn}></ha-icon>`;
|
return html`<ha-icon .icon=${icn}></ha-icon>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._renderFallback();
|
return this._renderFallback();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,8 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
||||||
import { describeCondition, describeTrigger } from "../../data/automation_i18n";
|
import { describeCondition, describeTrigger } from "../../data/automation_i18n";
|
||||||
import {
|
import { fullEntitiesContext, labelsContext } from "../../data/context";
|
||||||
floorsContext,
|
|
||||||
fullEntitiesContext,
|
|
||||||
labelsContext,
|
|
||||||
} from "../../data/context";
|
|
||||||
import type { EntityRegistryEntry } from "../../data/entity/entity_registry";
|
import type { EntityRegistryEntry } from "../../data/entity/entity_registry";
|
||||||
import type { FloorRegistryEntry } from "../../data/floor_registry";
|
|
||||||
import type { LabelRegistryEntry } from "../../data/label/label_registry";
|
import type { LabelRegistryEntry } from "../../data/label/label_registry";
|
||||||
import type { LogbookEntry } from "../../data/logbook";
|
import type { LogbookEntry } from "../../data/logbook";
|
||||||
import { describeAction } from "../../data/script_i18n";
|
import { describeAction } from "../../data/script_i18n";
|
||||||
@@ -63,10 +58,6 @@ export class HaTracePathDetails extends LitElement {
|
|||||||
@consume({ context: labelsContext, subscribe: true })
|
@consume({ context: labelsContext, subscribe: true })
|
||||||
_labelReg!: LabelRegistryEntry[];
|
_labelReg!: LabelRegistryEntry[];
|
||||||
|
|
||||||
@state()
|
|
||||||
@consume({ context: floorsContext, subscribe: true })
|
|
||||||
_floorReg!: Record<string, FloorRegistryEntry>;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="padded-box trace-info">
|
<div class="padded-box trace-info">
|
||||||
@@ -193,8 +184,6 @@ export class HaTracePathDetails extends LitElement {
|
|||||||
${describeAction(
|
${describeAction(
|
||||||
this.hass,
|
this.hass,
|
||||||
this._entityReg,
|
this._entityReg,
|
||||||
this._labelReg,
|
|
||||||
this._floorReg,
|
|
||||||
currentDetail
|
currentDetail
|
||||||
)}
|
)}
|
||||||
</h2>`
|
</h2>`
|
||||||
|
|||||||
@@ -15,14 +15,8 @@ import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_tim
|
|||||||
import { relativeTime } from "../../common/datetime/relative_time";
|
import { relativeTime } from "../../common/datetime/relative_time";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { toggleAttribute } from "../../common/dom/toggle_attribute";
|
import { toggleAttribute } from "../../common/dom/toggle_attribute";
|
||||||
import {
|
import { fullEntitiesContext } from "../../data/context";
|
||||||
floorsContext,
|
|
||||||
fullEntitiesContext,
|
|
||||||
labelsContext,
|
|
||||||
} from "../../data/context";
|
|
||||||
import type { EntityRegistryEntry } from "../../data/entity/entity_registry";
|
import type { EntityRegistryEntry } from "../../data/entity/entity_registry";
|
||||||
import type { FloorRegistryEntry } from "../../data/floor_registry";
|
|
||||||
import type { LabelRegistryEntry } from "../../data/label/label_registry";
|
|
||||||
import type { LogbookEntry } from "../../data/logbook";
|
import type { LogbookEntry } from "../../data/logbook";
|
||||||
import type {
|
import type {
|
||||||
ChooseAction,
|
ChooseAction,
|
||||||
@@ -197,8 +191,6 @@ class ActionRenderer {
|
|||||||
constructor(
|
constructor(
|
||||||
private hass: HomeAssistant,
|
private hass: HomeAssistant,
|
||||||
private entityReg: EntityRegistryEntry[],
|
private entityReg: EntityRegistryEntry[],
|
||||||
private labelReg: LabelRegistryEntry[],
|
|
||||||
private floorReg: Record<string, FloorRegistryEntry>,
|
|
||||||
private entries: TemplateResult[],
|
private entries: TemplateResult[],
|
||||||
private trace: AutomationTraceExtended,
|
private trace: AutomationTraceExtended,
|
||||||
private logbookRenderer: LogbookRenderer,
|
private logbookRenderer: LogbookRenderer,
|
||||||
@@ -313,14 +305,7 @@ class ActionRenderer {
|
|||||||
|
|
||||||
this._renderEntry(
|
this._renderEntry(
|
||||||
path,
|
path,
|
||||||
describeAction(
|
describeAction(this.hass, this.entityReg, data, actionType),
|
||||||
this.hass,
|
|
||||||
this.entityReg,
|
|
||||||
this.labelReg,
|
|
||||||
this.floorReg,
|
|
||||||
data,
|
|
||||||
actionType
|
|
||||||
),
|
|
||||||
undefined,
|
undefined,
|
||||||
data.enabled === false
|
data.enabled === false
|
||||||
);
|
);
|
||||||
@@ -485,13 +470,7 @@ class ActionRenderer {
|
|||||||
|
|
||||||
const name =
|
const name =
|
||||||
repeatConfig.alias ||
|
repeatConfig.alias ||
|
||||||
describeAction(
|
describeAction(this.hass, this.entityReg, repeatConfig);
|
||||||
this.hass,
|
|
||||||
this.entityReg,
|
|
||||||
this.labelReg,
|
|
||||||
this.floorReg,
|
|
||||||
repeatConfig
|
|
||||||
);
|
|
||||||
|
|
||||||
this._renderEntry(repeatPath, name, undefined, disabled);
|
this._renderEntry(repeatPath, name, undefined, disabled);
|
||||||
|
|
||||||
@@ -585,14 +564,7 @@ class ActionRenderer {
|
|||||||
this._renderEntry(
|
this._renderEntry(
|
||||||
sequencePath,
|
sequencePath,
|
||||||
sequenceConfig.alias ||
|
sequenceConfig.alias ||
|
||||||
describeAction(
|
describeAction(this.hass, this.entityReg, sequenceConfig, "sequence"),
|
||||||
this.hass,
|
|
||||||
this.entityReg,
|
|
||||||
this.labelReg,
|
|
||||||
this.floorReg,
|
|
||||||
sequenceConfig,
|
|
||||||
"sequence"
|
|
||||||
),
|
|
||||||
undefined,
|
undefined,
|
||||||
sequenceConfig.enabled === false
|
sequenceConfig.enabled === false
|
||||||
);
|
);
|
||||||
@@ -683,14 +655,6 @@ export class HaAutomationTracer extends LitElement {
|
|||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entityReg!: EntityRegistryEntry[];
|
_entityReg!: EntityRegistryEntry[];
|
||||||
|
|
||||||
@state()
|
|
||||||
@consume({ context: labelsContext, subscribe: true })
|
|
||||||
_labelReg!: LabelRegistryEntry[];
|
|
||||||
|
|
||||||
@state()
|
|
||||||
@consume({ context: floorsContext, subscribe: true })
|
|
||||||
_floorReg!: Record<string, FloorRegistryEntry>;
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.trace) {
|
if (!this.trace) {
|
||||||
return nothing;
|
return nothing;
|
||||||
@@ -707,8 +671,6 @@ export class HaAutomationTracer extends LitElement {
|
|||||||
const actionRenderer = new ActionRenderer(
|
const actionRenderer = new ActionRenderer(
|
||||||
this.hass,
|
this.hass,
|
||||||
this._entityReg,
|
this._entityReg,
|
||||||
this._labelReg,
|
|
||||||
this._floorReg,
|
|
||||||
entries,
|
entries,
|
||||||
this.trace,
|
this.trace,
|
||||||
logbookRenderer,
|
logbookRenderer,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ensureArray } from "../common/array/ensure-array";
|
import { ensureArray } from "../common/array/ensure-array";
|
||||||
import { formatNumericDuration } from "../common/datetime/format_duration";
|
import { formatNumericDuration } from "../common/datetime/format_duration";
|
||||||
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
||||||
import { computeDeviceNameDisplay } from "../common/entity/compute_device_name";
|
|
||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
import { formatListWithAnds } from "../common/string/format-list";
|
import { formatListWithAnds } from "../common/string/format-list";
|
||||||
import { isTemplate } from "../common/string/has-template";
|
import { isTemplate } from "../common/string/has-template";
|
||||||
@@ -10,13 +9,7 @@ import type { Condition } from "./automation";
|
|||||||
import { describeCondition } from "./automation_i18n";
|
import { describeCondition } from "./automation_i18n";
|
||||||
import { localizeDeviceAutomationAction } from "./device/device_automation";
|
import { localizeDeviceAutomationAction } from "./device/device_automation";
|
||||||
import type { EntityRegistryEntry } from "./entity/entity_registry";
|
import type { EntityRegistryEntry } from "./entity/entity_registry";
|
||||||
import {
|
|
||||||
computeEntityRegistryName,
|
|
||||||
entityRegistryById,
|
|
||||||
} from "./entity/entity_registry";
|
|
||||||
import type { FloorRegistryEntry } from "./floor_registry";
|
|
||||||
import { domainToName } from "./integration";
|
import { domainToName } from "./integration";
|
||||||
import type { LabelRegistryEntry } from "./label/label_registry";
|
|
||||||
import type {
|
import type {
|
||||||
ActionType,
|
ActionType,
|
||||||
ActionTypes,
|
ActionTypes,
|
||||||
@@ -41,8 +34,6 @@ const actionTranslationBaseKey =
|
|||||||
export const describeAction = <T extends ActionType>(
|
export const describeAction = <T extends ActionType>(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityRegistry: EntityRegistryEntry[],
|
entityRegistry: EntityRegistryEntry[],
|
||||||
labelRegistry: LabelRegistryEntry[],
|
|
||||||
floorRegistry: Record<string, FloorRegistryEntry>,
|
|
||||||
action: ActionTypes[T],
|
action: ActionTypes[T],
|
||||||
actionType?: T,
|
actionType?: T,
|
||||||
ignoreAlias = false
|
ignoreAlias = false
|
||||||
@@ -51,8 +42,6 @@ export const describeAction = <T extends ActionType>(
|
|||||||
const description = tryDescribeAction(
|
const description = tryDescribeAction(
|
||||||
hass,
|
hass,
|
||||||
entityRegistry,
|
entityRegistry,
|
||||||
labelRegistry,
|
|
||||||
floorRegistry,
|
|
||||||
action,
|
action,
|
||||||
actionType,
|
actionType,
|
||||||
ignoreAlias
|
ignoreAlias
|
||||||
@@ -75,8 +64,6 @@ export const describeAction = <T extends ActionType>(
|
|||||||
const tryDescribeAction = <T extends ActionType>(
|
const tryDescribeAction = <T extends ActionType>(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityRegistry: EntityRegistryEntry[],
|
entityRegistry: EntityRegistryEntry[],
|
||||||
labelRegistry: LabelRegistryEntry[],
|
|
||||||
floorRegistry: Record<string, FloorRegistryEntry>,
|
|
||||||
action: ActionTypes[T],
|
action: ActionTypes[T],
|
||||||
actionType?: T,
|
actionType?: T,
|
||||||
ignoreAlias = false
|
ignoreAlias = false
|
||||||
@@ -100,107 +87,6 @@ const tryDescribeAction = <T extends ActionType>(
|
|||||||
{ name: "target" }
|
{ name: "target" }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else if (targetOrData) {
|
|
||||||
for (const [key, name] of Object.entries({
|
|
||||||
area_id: "areas",
|
|
||||||
device_id: "devices",
|
|
||||||
entity_id: "entities",
|
|
||||||
floor_id: "floors",
|
|
||||||
label_id: "labels",
|
|
||||||
})) {
|
|
||||||
if (!(key in targetOrData)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const keyConf: string[] = ensureArray(targetOrData[key]) || [];
|
|
||||||
|
|
||||||
for (const targetThing of keyConf) {
|
|
||||||
if (isTemplate(targetThing)) {
|
|
||||||
targets.push(
|
|
||||||
hass.localize(
|
|
||||||
`${actionTranslationBaseKey}.service.description.target_template`,
|
|
||||||
{ name }
|
|
||||||
)
|
|
||||||
);
|
|
||||||
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(entityRegistry)[targetThing];
|
|
||||||
if (entityReg) {
|
|
||||||
targets.push(
|
|
||||||
computeEntityRegistryName(hass, entityReg) || targetThing
|
|
||||||
);
|
|
||||||
} else if (targetThing === "all") {
|
|
||||||
targets.push(
|
|
||||||
hass.localize(
|
|
||||||
`${actionTranslationBaseKey}.service.description.target_every_entity`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
targets.push(
|
|
||||||
hass.localize(
|
|
||||||
`${actionTranslationBaseKey}.service.description.target_unknown_entity`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (key === "device_id") {
|
|
||||||
const device = hass.devices[targetThing];
|
|
||||||
if (device) {
|
|
||||||
targets.push(computeDeviceNameDisplay(device, hass));
|
|
||||||
} else {
|
|
||||||
targets.push(
|
|
||||||
hass.localize(
|
|
||||||
`${actionTranslationBaseKey}.service.description.target_unknown_device`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (key === "area_id") {
|
|
||||||
const area = hass.areas[targetThing];
|
|
||||||
if (area?.name) {
|
|
||||||
targets.push(area.name);
|
|
||||||
} else {
|
|
||||||
targets.push(
|
|
||||||
hass.localize(
|
|
||||||
`${actionTranslationBaseKey}.service.description.target_unknown_area`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (key === "floor_id") {
|
|
||||||
const floor = floorRegistry[targetThing] ?? undefined;
|
|
||||||
if (floor?.name) {
|
|
||||||
targets.push(floor.name);
|
|
||||||
} else {
|
|
||||||
targets.push(
|
|
||||||
hass.localize(
|
|
||||||
`${actionTranslationBaseKey}.service.description.target_unknown_floor`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (key === "label_id") {
|
|
||||||
const label = labelRegistry.find(
|
|
||||||
(lbl) => lbl.label_id === targetThing
|
|
||||||
);
|
|
||||||
if (label?.name) {
|
|
||||||
targets.push(label.name);
|
|
||||||
} else {
|
|
||||||
targets.push(
|
|
||||||
hass.localize(
|
|
||||||
`${actionTranslationBaseKey}.service.description.target_unknown_label`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
targets.push(targetThing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -229,26 +115,20 @@ const tryDescribeAction = <T extends ActionType>(
|
|||||||
|
|
||||||
if (config.metadata) {
|
if (config.metadata) {
|
||||||
return hass.localize(
|
return hass.localize(
|
||||||
targets.length
|
`${actionTranslationBaseKey}.service.description.service_name_no_targets`,
|
||||||
? `${actionTranslationBaseKey}.service.description.service_name`
|
|
||||||
: `${actionTranslationBaseKey}.service.description.service_name_no_targets`,
|
|
||||||
{
|
{
|
||||||
domain: domainToName(hass.localize, domain),
|
domain: domainToName(hass.localize, domain),
|
||||||
name: service || config.action,
|
name: service || config.action,
|
||||||
targets: formatListWithAnds(hass.locale, targets),
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hass.localize(
|
return hass.localize(
|
||||||
targets.length
|
`${actionTranslationBaseKey}.service.description.service_based_on_name_no_targets`,
|
||||||
? `${actionTranslationBaseKey}.service.description.service_based_on_name`
|
|
||||||
: `${actionTranslationBaseKey}.service.description.service_based_on_name_no_targets`,
|
|
||||||
{
|
{
|
||||||
name: service
|
name: service
|
||||||
? `${domainToName(hass.localize, domain)}: ${service}`
|
? `${domainToName(hass.localize, domain)}: ${service}`
|
||||||
: config.action,
|
: config.action,
|
||||||
targets: formatListWithAnds(hass.locale, targets),
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
mdiStopCircleOutline,
|
mdiStopCircleOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
|
import type { HassServiceTarget } from "home-assistant-js-websocket";
|
||||||
import { dump } from "js-yaml";
|
import { dump } from "js-yaml";
|
||||||
import type { PropertyValues, TemplateResult } from "lit";
|
import type { PropertyValues, TemplateResult } from "lit";
|
||||||
import { LitElement, html, nothing } from "lit";
|
import { LitElement, html, nothing } from "lit";
|
||||||
@@ -53,18 +54,13 @@ import type {
|
|||||||
} from "../../../../data/automation";
|
} from "../../../../data/automation";
|
||||||
import { CONDITION_BUILDING_BLOCKS } from "../../../../data/condition";
|
import { CONDITION_BUILDING_BLOCKS } from "../../../../data/condition";
|
||||||
import { validateConfig } from "../../../../data/config";
|
import { validateConfig } from "../../../../data/config";
|
||||||
import {
|
import { fullEntitiesContext } from "../../../../data/context";
|
||||||
floorsContext,
|
|
||||||
fullEntitiesContext,
|
|
||||||
labelsContext,
|
|
||||||
} from "../../../../data/context";
|
|
||||||
import type { EntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
import type { EntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||||
import type { FloorRegistryEntry } from "../../../../data/floor_registry";
|
|
||||||
import type { LabelRegistryEntry } from "../../../../data/label/label_registry";
|
|
||||||
import type {
|
import type {
|
||||||
Action,
|
Action,
|
||||||
NonConditionAction,
|
NonConditionAction,
|
||||||
RepeatAction,
|
RepeatAction,
|
||||||
|
ServiceAction,
|
||||||
} from "../../../../data/script";
|
} from "../../../../data/script";
|
||||||
import { getActionType, isAction } from "../../../../data/script";
|
import { getActionType, isAction } from "../../../../data/script";
|
||||||
import { describeAction } from "../../../../data/script_i18n";
|
import { describeAction } from "../../../../data/script_i18n";
|
||||||
@@ -78,6 +74,7 @@ import { isMac } from "../../../../util/is_mac";
|
|||||||
import { showToast } from "../../../../util/toast";
|
import { showToast } from "../../../../util/toast";
|
||||||
import "../ha-automation-editor-warning";
|
import "../ha-automation-editor-warning";
|
||||||
import { overflowStyles, rowStyles } from "../styles";
|
import { overflowStyles, rowStyles } from "../styles";
|
||||||
|
import "../target/ha-automation-row-targets";
|
||||||
import "./ha-automation-action-editor";
|
import "./ha-automation-action-editor";
|
||||||
import type HaAutomationActionEditor from "./ha-automation-action-editor";
|
import type HaAutomationActionEditor from "./ha-automation-action-editor";
|
||||||
import "./types/ha-automation-action-choose";
|
import "./types/ha-automation-action-choose";
|
||||||
@@ -176,14 +173,6 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entityReg!: EntityRegistryEntry[];
|
_entityReg!: EntityRegistryEntry[];
|
||||||
|
|
||||||
@state()
|
|
||||||
@consume({ context: labelsContext, subscribe: true })
|
|
||||||
_labelReg!: LabelRegistryEntry[];
|
|
||||||
|
|
||||||
@state()
|
|
||||||
@consume({ context: floorsContext, subscribe: true })
|
|
||||||
_floorReg!: Record<string, FloorRegistryEntry>;
|
|
||||||
|
|
||||||
@state() private _uiModeAvailable = true;
|
@state() private _uiModeAvailable = true;
|
||||||
|
|
||||||
@state() private _yamlMode = false;
|
@state() private _yamlMode = false;
|
||||||
@@ -263,14 +252,11 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
`}
|
`}
|
||||||
<h3 slot="header">
|
<h3 slot="header">
|
||||||
${capitalizeFirstLetter(
|
${capitalizeFirstLetter(
|
||||||
describeAction(
|
describeAction(this.hass, this._entityReg, this.action)
|
||||||
this.hass,
|
|
||||||
this._entityReg,
|
|
||||||
this._labelReg,
|
|
||||||
this._floorReg,
|
|
||||||
this.action
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
|
${type === "service" && "target" in this.action
|
||||||
|
? this._renderTargets((this.action as ServiceAction).target)
|
||||||
|
: nothing}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<slot name="icons" slot="icons"></slot>
|
<slot name="icons" slot="icons"></slot>
|
||||||
@@ -556,6 +542,14 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderTargets = memoizeOne(
|
||||||
|
(target?: HassServiceTarget) =>
|
||||||
|
html`<ha-automation-row-targets
|
||||||
|
.hass=${this.hass}
|
||||||
|
.target=${target}
|
||||||
|
></ha-automation-row-targets>`
|
||||||
|
);
|
||||||
|
|
||||||
private _onValueChange(event: CustomEvent) {
|
private _onValueChange(event: CustomEvent) {
|
||||||
// reload sidebar if sort, deleted,... happend
|
// reload sidebar if sort, deleted,... happend
|
||||||
if (this._selected && this.optionsInSidebar) {
|
if (this._selected && this.optionsInSidebar) {
|
||||||
@@ -668,15 +662,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
),
|
),
|
||||||
inputType: "string",
|
inputType: "string",
|
||||||
placeholder: capitalizeFirstLetter(
|
placeholder: capitalizeFirstLetter(
|
||||||
describeAction(
|
describeAction(this.hass, this._entityReg, this.action, undefined, true)
|
||||||
this.hass,
|
|
||||||
this._entityReg,
|
|
||||||
this._labelReg,
|
|
||||||
this._floorReg,
|
|
||||||
this.action,
|
|
||||||
undefined,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
defaultValue: this.action.alias,
|
defaultValue: this.action.alias,
|
||||||
confirmText: this.hass.localize("ui.common.submit"),
|
confirmText: this.hass.localize("ui.common.submit"),
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import memoizeOne from "memoize-one";
|
|||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { mainWindow } from "../../../common/dom/get_main_window";
|
import { mainWindow } from "../../../common/dom/get_main_window";
|
||||||
import { computeAreaName } from "../../../common/entity/compute_area_name";
|
import { computeAreaName } from "../../../common/entity/compute_area_name";
|
||||||
import { computeDeviceName } from "../../../common/entity/compute_device_name";
|
|
||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
import { computeEntityNameList } from "../../../common/entity/compute_entity_name_display";
|
import { computeEntityNameList } from "../../../common/entity/compute_entity_name_display";
|
||||||
import { computeFloorName } from "../../../common/entity/compute_floor_name";
|
import { computeFloorName } from "../../../common/entity/compute_floor_name";
|
||||||
@@ -123,6 +122,7 @@ import "./add-automation-element/ha-automation-add-items";
|
|||||||
import "./add-automation-element/ha-automation-add-search";
|
import "./add-automation-element/ha-automation-add-search";
|
||||||
import type { AddAutomationElementDialogParams } from "./show-add-automation-element-dialog";
|
import type { AddAutomationElementDialogParams } from "./show-add-automation-element-dialog";
|
||||||
import { PASTE_VALUE } from "./show-add-automation-element-dialog";
|
import { PASTE_VALUE } from "./show-add-automation-element-dialog";
|
||||||
|
import { getTargetText } from "./target/get_target_text";
|
||||||
|
|
||||||
const TYPES = {
|
const TYPES = {
|
||||||
trigger: { collections: TRIGGER_COLLECTIONS, icons: TRIGGER_ICONS },
|
trigger: { collections: TRIGGER_COLLECTIONS, icons: TRIGGER_ICONS },
|
||||||
@@ -1393,8 +1393,8 @@ class DialogAddAutomationElement
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
private _getLabel = memoizeOne((labelId) =>
|
private _getLabel = memoizeOne((id: string) =>
|
||||||
this._labelRegistry?.find(({ label_id }) => label_id === labelId)
|
this._labelRegistry?.find(({ label_id }) => label_id === id)
|
||||||
);
|
);
|
||||||
|
|
||||||
private _getDomainType(domain: string) {
|
private _getDomainType(domain: string) {
|
||||||
@@ -1926,32 +1926,12 @@ class DialogAddAutomationElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (targetId) {
|
if (targetId) {
|
||||||
if (targetType === "floor") {
|
return getTargetText(
|
||||||
return computeFloorName(this.hass.floors[targetId]) || targetId;
|
this.hass,
|
||||||
}
|
targetType as "floor" | "area" | "device" | "entity" | "label",
|
||||||
if (targetType === "area") {
|
targetId,
|
||||||
return computeAreaName(this.hass.areas[targetId]) || targetId;
|
this._getLabel
|
||||||
}
|
);
|
||||||
if (targetType === "device") {
|
|
||||||
return computeDeviceName(this.hass.devices[targetId]) || targetId;
|
|
||||||
}
|
|
||||||
if (targetType === "entity" && this.hass.states[targetId]) {
|
|
||||||
const stateObj = this.hass.states[targetId];
|
|
||||||
const [entityName, deviceName] = computeEntityNameList(
|
|
||||||
stateObj,
|
|
||||||
[{ type: "entity" }, { type: "device" }, { type: "area" }],
|
|
||||||
this.hass.entities,
|
|
||||||
this.hass.devices,
|
|
||||||
this.hass.areas,
|
|
||||||
this.hass.floors
|
|
||||||
);
|
|
||||||
|
|
||||||
return entityName || deviceName || targetId;
|
|
||||||
}
|
|
||||||
if (targetType === "label") {
|
|
||||||
const label = this._getLabel(targetId);
|
|
||||||
return label?.name || targetId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import {
|
import { mdiInformationOutline, mdiPlus } from "@mdi/js";
|
||||||
mdiInformationOutline,
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
mdiLabel,
|
|
||||||
mdiPlus,
|
|
||||||
mdiTextureBox,
|
|
||||||
} from "@mdi/js";
|
|
||||||
import { LitElement, css, html, nothing, type TemplateResult } from "lit";
|
|
||||||
import {
|
import {
|
||||||
customElement,
|
customElement,
|
||||||
eventOptions,
|
eventOptions,
|
||||||
@@ -17,17 +12,15 @@ import { repeat } from "lit/directives/repeat";
|
|||||||
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 { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import "../../../../components/entity/state-badge";
|
|
||||||
import "../../../../components/ha-domain-icon";
|
|
||||||
import "../../../../components/ha-floor-icon";
|
|
||||||
import "../../../../components/ha-icon-next";
|
|
||||||
import "../../../../components/ha-md-list";
|
import "../../../../components/ha-md-list";
|
||||||
import "../../../../components/ha-md-list-item";
|
import "../../../../components/ha-md-list-item";
|
||||||
import "../../../../components/ha-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
import "../../../../components/ha-tooltip";
|
import "../../../../components/ha-tooltip";
|
||||||
import type { ConfigEntry } from "../../../../data/config_entries";
|
import type { ConfigEntry } from "../../../../data/config_entries";
|
||||||
|
import type { LabelRegistryEntry } from "../../../../data/label/label_registry";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import type { AddAutomationElementListItem } from "../add-automation-element-dialog";
|
import type { AddAutomationElementListItem } from "../add-automation-element-dialog";
|
||||||
|
import { getTargetIcon } from "../target/get_target_icon";
|
||||||
|
|
||||||
type Target = [string, string | undefined, string | undefined];
|
type Target = [string, string | undefined, string | undefined];
|
||||||
|
|
||||||
@@ -50,7 +43,7 @@ export class HaAutomationAddItems extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public getLabel!: (
|
@property({ attribute: false }) public getLabel!: (
|
||||||
id: string
|
id: string
|
||||||
) => { name: string; icon?: string } | undefined;
|
) => LabelRegistryEntry | undefined;
|
||||||
|
|
||||||
@property({ attribute: false }) public configEntryLookup: Record<
|
@property({ attribute: false }) public configEntryLookup: Record<
|
||||||
string,
|
string,
|
||||||
@@ -164,72 +157,17 @@ export class HaAutomationAddItems extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`<div class="selected-target">
|
return html`<div class="selected-target">
|
||||||
${this._getSelectedTargetIcon(target[0], target[1])}
|
${getTargetIcon(
|
||||||
|
this.hass,
|
||||||
|
target[0],
|
||||||
|
target[1],
|
||||||
|
this.configEntryLookup,
|
||||||
|
this.getLabel
|
||||||
|
)}
|
||||||
<div class="label">${target[2]}</div>
|
<div class="label">${target[2]}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
private _getSelectedTargetIcon(
|
|
||||||
targetType: string,
|
|
||||||
targetId: string | undefined
|
|
||||||
): TemplateResult | typeof nothing {
|
|
||||||
if (!targetId) {
|
|
||||||
return nothing;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetType === "floor") {
|
|
||||||
return html`<ha-floor-icon
|
|
||||||
.floor=${this.hass.floors[targetId]}
|
|
||||||
></ha-floor-icon>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetType === "area" && this.hass.areas[targetId]) {
|
|
||||||
const area = this.hass.areas[targetId];
|
|
||||||
if (area.icon) {
|
|
||||||
return html`<ha-icon .icon=${area.icon}></ha-icon>`;
|
|
||||||
}
|
|
||||||
return html`<ha-svg-icon .path=${mdiTextureBox}></ha-svg-icon>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetType === "device" && this.hass.devices[targetId]) {
|
|
||||||
const device = this.hass.devices[targetId];
|
|
||||||
const configEntry = device.primary_config_entry
|
|
||||||
? this.configEntryLookup[device.primary_config_entry]
|
|
||||||
: undefined;
|
|
||||||
const domain = configEntry?.domain;
|
|
||||||
|
|
||||||
if (domain) {
|
|
||||||
return html`<ha-domain-icon
|
|
||||||
slot="start"
|
|
||||||
.hass=${this.hass}
|
|
||||||
.domain=${domain}
|
|
||||||
brand-fallback
|
|
||||||
></ha-domain-icon>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetType === "entity" && this.hass.states[targetId]) {
|
|
||||||
const stateObj = this.hass.states[targetId];
|
|
||||||
if (stateObj) {
|
|
||||||
return html`<state-badge
|
|
||||||
.stateObj=${stateObj}
|
|
||||||
.hass=${this.hass}
|
|
||||||
.stateColor=${false}
|
|
||||||
></state-badge>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetType === "label") {
|
|
||||||
const label = this.getLabel(targetId);
|
|
||||||
if (label?.icon) {
|
|
||||||
return html`<ha-icon .icon=${label.icon}></ha-icon>`;
|
|
||||||
}
|
|
||||||
return html`<ha-svg-icon .path=${mdiLabel}></ha-svg-icon>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nothing;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _selected(ev) {
|
private _selected(ev) {
|
||||||
const item = ev.currentTarget;
|
const item = ev.currentTarget;
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
@@ -335,10 +273,6 @@ export class HaAutomationAddItems extends LitElement {
|
|||||||
border-bottom: 1px solid var(--ha-color-border-neutral-quiet);
|
border-bottom: 1px solid var(--ha-color-border-neutral-quiet);
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-icon-next {
|
|
||||||
width: var(--ha-space-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-svg-icon.plus {
|
ha-svg-icon.plus {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
@@ -362,16 +296,11 @@ export class HaAutomationAddItems extends LitElement {
|
|||||||
|
|
||||||
.selected-target ha-icon,
|
.selected-target ha-icon,
|
||||||
.selected-target ha-svg-icon,
|
.selected-target ha-svg-icon,
|
||||||
.selected-target state-badge,
|
|
||||||
.selected-target ha-domain-icon {
|
.selected-target ha-domain-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: var(--ha-space-1) 0;
|
padding: var(--ha-space-1) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected-target state-badge {
|
|
||||||
--mdc-icon-size: 24px;
|
|
||||||
}
|
|
||||||
.selected-target state-badge,
|
|
||||||
.selected-target ha-floor-icon {
|
.selected-target ha-floor-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
mdiStopCircleOutline,
|
mdiStopCircleOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
|
import type { HassServiceTarget } from "home-assistant-js-websocket";
|
||||||
import { dump } from "js-yaml";
|
import { dump } from "js-yaml";
|
||||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
@@ -43,6 +44,7 @@ import type {
|
|||||||
AutomationClipboard,
|
AutomationClipboard,
|
||||||
Condition,
|
Condition,
|
||||||
ConditionSidebarConfig,
|
ConditionSidebarConfig,
|
||||||
|
PlatformCondition,
|
||||||
} from "../../../../data/automation";
|
} from "../../../../data/automation";
|
||||||
import { isCondition, testCondition } from "../../../../data/automation";
|
import { isCondition, testCondition } from "../../../../data/automation";
|
||||||
import { describeCondition } from "../../../../data/automation_i18n";
|
import { describeCondition } from "../../../../data/automation_i18n";
|
||||||
@@ -60,6 +62,7 @@ import { isMac } from "../../../../util/is_mac";
|
|||||||
import { showToast } from "../../../../util/toast";
|
import { showToast } from "../../../../util/toast";
|
||||||
import "../ha-automation-editor-warning";
|
import "../ha-automation-editor-warning";
|
||||||
import { overflowStyles, rowStyles } from "../styles";
|
import { overflowStyles, rowStyles } from "../styles";
|
||||||
|
import "../target/ha-automation-row-targets";
|
||||||
import "./ha-automation-condition-editor";
|
import "./ha-automation-condition-editor";
|
||||||
import type HaAutomationConditionEditor from "./ha-automation-condition-editor";
|
import type HaAutomationConditionEditor from "./ha-automation-condition-editor";
|
||||||
import "./types/ha-automation-condition-and";
|
import "./types/ha-automation-condition-and";
|
||||||
@@ -191,6 +194,10 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
${capitalizeFirstLetter(
|
${capitalizeFirstLetter(
|
||||||
describeCondition(this.condition, this.hass, this._entityReg)
|
describeCondition(this.condition, this.hass, this._entityReg)
|
||||||
)}
|
)}
|
||||||
|
${"target" in
|
||||||
|
(this.conditionDescriptions[this.condition.condition] || {})
|
||||||
|
? this._renderTargets((this.condition as PlatformCondition).target)
|
||||||
|
: nothing}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<slot name="icons" slot="icons"></slot>
|
<slot name="icons" slot="icons"></slot>
|
||||||
@@ -475,6 +482,14 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderTargets = memoizeOne(
|
||||||
|
(target?: HassServiceTarget) =>
|
||||||
|
html`<ha-automation-row-targets
|
||||||
|
.hass=${this.hass}
|
||||||
|
.target=${target}
|
||||||
|
></ha-automation-row-targets>`
|
||||||
|
);
|
||||||
|
|
||||||
protected firstUpdated(changedProperties: PropertyValues): void {
|
protected firstUpdated(changedProperties: PropertyValues): void {
|
||||||
super.firstUpdated(changedProperties);
|
super.firstUpdated(changedProperties);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ export const rowStyles = css`
|
|||||||
h3 {
|
h3 {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
font-weight: inherit;
|
font-weight: inherit;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--ha-space-2);
|
||||||
|
padding: var(--ha-space-2) 0;
|
||||||
|
min-height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-card {
|
ha-card {
|
||||||
|
|||||||
69
src/panels/config/automation/target/get_target_icon.ts
Normal file
69
src/panels/config/automation/target/get_target_icon.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { mdiLabel, mdiTextureBox } from "@mdi/js";
|
||||||
|
import { html, nothing, type TemplateResult } from "lit";
|
||||||
|
import "../../../../components/ha-domain-icon";
|
||||||
|
import "../../../../components/ha-floor-icon";
|
||||||
|
import "../../../../components/ha-icon";
|
||||||
|
import "../../../../components/ha-state-icon";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
|
import type { ConfigEntry } from "../../../../data/config_entries";
|
||||||
|
import type { LabelRegistryEntry } from "../../../../data/label/label_registry";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
|
export const getTargetIcon = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
targetType: string,
|
||||||
|
targetId: string | undefined,
|
||||||
|
configEntryLookup: Record<string, ConfigEntry>,
|
||||||
|
getLabel?: (id: string) => LabelRegistryEntry | undefined
|
||||||
|
): TemplateResult | typeof nothing => {
|
||||||
|
if (!targetId) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetType === "floor" && hass.floors[targetId]) {
|
||||||
|
return html`<ha-floor-icon
|
||||||
|
.floor=${hass.floors[targetId]}
|
||||||
|
></ha-floor-icon>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetType === "area") {
|
||||||
|
const area = hass.areas[targetId];
|
||||||
|
if (area?.icon) {
|
||||||
|
return html`<ha-icon .icon=${area.icon}></ha-icon>`;
|
||||||
|
}
|
||||||
|
return html`<ha-svg-icon .path=${mdiTextureBox}></ha-svg-icon>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetType === "device" && hass.devices[targetId]) {
|
||||||
|
const device = hass.devices[targetId];
|
||||||
|
const configEntry = device.primary_config_entry
|
||||||
|
? configEntryLookup[device.primary_config_entry]
|
||||||
|
: undefined;
|
||||||
|
const domain = configEntry?.domain;
|
||||||
|
|
||||||
|
if (domain) {
|
||||||
|
return html`<ha-domain-icon
|
||||||
|
.hass=${hass}
|
||||||
|
.domain=${domain}
|
||||||
|
brand-fallback
|
||||||
|
></ha-domain-icon>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetType === "entity" && hass.states[targetId]) {
|
||||||
|
return html`<ha-state-icon
|
||||||
|
.hass=${hass}
|
||||||
|
.stateObj=${hass.states[targetId]}
|
||||||
|
></ha-state-icon>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetType === "label" && getLabel) {
|
||||||
|
const label = getLabel(targetId);
|
||||||
|
if (label?.icon) {
|
||||||
|
return html`<ha-icon .icon=${label.icon}></ha-icon>`;
|
||||||
|
}
|
||||||
|
return html`<ha-svg-icon .path=${mdiLabel}></ha-svg-icon>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nothing;
|
||||||
|
};
|
||||||
68
src/panels/config/automation/target/get_target_text.ts
Normal file
68
src/panels/config/automation/target/get_target_text.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { computeAreaName } from "../../../../common/entity/compute_area_name";
|
||||||
|
import { computeDeviceName } from "../../../../common/entity/compute_device_name";
|
||||||
|
import { computeEntityNameList } from "../../../../common/entity/compute_entity_name_display";
|
||||||
|
import { computeFloorName } from "../../../../common/entity/compute_floor_name";
|
||||||
|
import type { LabelRegistryEntry } from "../../../../data/label/label_registry";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
|
export const getTargetText = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
targetType: "floor" | "area" | "device" | "entity" | "label",
|
||||||
|
targetId: string,
|
||||||
|
getLabel?: (id: string) => LabelRegistryEntry | undefined
|
||||||
|
): string => {
|
||||||
|
if (targetType === "floor") {
|
||||||
|
return (
|
||||||
|
(hass.floors[targetId] && computeFloorName(hass.floors[targetId])) ||
|
||||||
|
hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.service.description.target_unknown_floor"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (targetType === "area") {
|
||||||
|
return (
|
||||||
|
(hass.areas[targetId] && computeAreaName(hass.areas[targetId])) ||
|
||||||
|
hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.service.description.target_unknown_area"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (targetType === "device") {
|
||||||
|
return (
|
||||||
|
(hass.devices[targetId] && computeDeviceName(hass.devices[targetId])) ||
|
||||||
|
hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.service.description.target_unknown_device"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (targetType === "entity" && hass.states[targetId]) {
|
||||||
|
const stateObj = hass.states[targetId];
|
||||||
|
const [entityName, deviceName] = computeEntityNameList(
|
||||||
|
stateObj,
|
||||||
|
[{ type: "entity" }, { type: "device" }, { type: "area" }],
|
||||||
|
hass.entities,
|
||||||
|
hass.devices,
|
||||||
|
hass.areas,
|
||||||
|
hass.floors
|
||||||
|
);
|
||||||
|
|
||||||
|
return entityName || deviceName || targetId;
|
||||||
|
}
|
||||||
|
if (targetType === "entity") {
|
||||||
|
return hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.service.description.target_unknown_entity"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetType === "label" && getLabel) {
|
||||||
|
const label = getLabel(targetId);
|
||||||
|
return (
|
||||||
|
label?.name ||
|
||||||
|
hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.service.description.target_unknown_label"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetId;
|
||||||
|
};
|
||||||
260
src/panels/config/automation/target/ha-automation-row-targets.ts
Normal file
260
src/panels/config/automation/target/ha-automation-row-targets.ts
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
import { consume } from "@lit/context";
|
||||||
|
import { mdiAlert, mdiFormatListBulleted, mdiShape } from "@mdi/js";
|
||||||
|
import type { HassServiceTarget } from "home-assistant-js-websocket";
|
||||||
|
import { LitElement, css, html, nothing, type TemplateResult } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { until } from "lit/directives/until";
|
||||||
|
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
|
import {
|
||||||
|
getConfigEntries,
|
||||||
|
type ConfigEntry,
|
||||||
|
} from "../../../../data/config_entries";
|
||||||
|
import {
|
||||||
|
areasContext,
|
||||||
|
devicesContext,
|
||||||
|
floorsContext,
|
||||||
|
labelsContext,
|
||||||
|
localizeContext,
|
||||||
|
statesContext,
|
||||||
|
} from "../../../../data/context";
|
||||||
|
import type { LabelRegistryEntry } from "../../../../data/label/label_registry";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import { getTargetIcon } from "./get_target_icon";
|
||||||
|
import { getTargetText } from "./get_target_text";
|
||||||
|
|
||||||
|
@customElement("ha-automation-row-targets")
|
||||||
|
export class HaAutomationRowTargets extends LitElement {
|
||||||
|
@property({ attribute: false })
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public target?: HassServiceTarget;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: localizeContext, subscribe: true })
|
||||||
|
private localize!: HomeAssistant["localize"];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: floorsContext, subscribe: true })
|
||||||
|
private floors!: HomeAssistant["floors"];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: areasContext, subscribe: true })
|
||||||
|
private areas!: HomeAssistant["areas"];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: devicesContext, subscribe: true })
|
||||||
|
private devices!: HomeAssistant["devices"];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: statesContext, subscribe: true })
|
||||||
|
private states!: HomeAssistant["states"];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: labelsContext, subscribe: true })
|
||||||
|
private _labelRegistry!: LabelRegistryEntry[];
|
||||||
|
|
||||||
|
private _configEntryLookup?: Record<string, ConfigEntry>;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const length = Object.keys(this.target || {}).length;
|
||||||
|
if (!length) {
|
||||||
|
return html`<span class="target">
|
||||||
|
<div class="label">
|
||||||
|
${this.localize(
|
||||||
|
"ui.panel.config.automation.editor.target_summary.no_target"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
const totalLength = Object.values(this.target || {}).reduce(
|
||||||
|
(acc, val) => acc + ensureArray(val).length,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (totalLength <= 5) {
|
||||||
|
const targets = Object.entries(this.target!).reduce<
|
||||||
|
["floor" | "area" | "device" | "entity" | "label", string][]
|
||||||
|
>((acc, [targetType, targetId]) => {
|
||||||
|
const type = targetType.replace("_id", "") as
|
||||||
|
| "floor"
|
||||||
|
| "area"
|
||||||
|
| "device"
|
||||||
|
| "entity"
|
||||||
|
| "label";
|
||||||
|
return [
|
||||||
|
...acc,
|
||||||
|
...ensureArray(targetId).map((id): [typeof type, string] => [
|
||||||
|
type,
|
||||||
|
id,
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return targets.map(
|
||||||
|
([targetType, targetId]) =>
|
||||||
|
html`<span class="target-wrapper">
|
||||||
|
${this._renderTarget(targetType, targetId)}
|
||||||
|
</span>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`<span class="target">
|
||||||
|
<ha-svg-icon .path=${mdiFormatListBulleted}></ha-svg-icon>
|
||||||
|
<div class="label">
|
||||||
|
${this.localize(
|
||||||
|
"ui.panel.config.automation.editor.target_summary.targets",
|
||||||
|
{
|
||||||
|
count: totalLength,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getLabel = (id: string) =>
|
||||||
|
this._labelRegistry?.find(({ label_id }) => label_id === id);
|
||||||
|
|
||||||
|
private _checkTargetExists(
|
||||||
|
targetType: "floor" | "area" | "device" | "entity" | "label",
|
||||||
|
targetId: string
|
||||||
|
): boolean {
|
||||||
|
if (targetType === "floor") {
|
||||||
|
return !!this.floors[targetId];
|
||||||
|
}
|
||||||
|
if (targetType === "area") {
|
||||||
|
return !!this.areas[targetId];
|
||||||
|
}
|
||||||
|
if (targetType === "device") {
|
||||||
|
return !!this.devices[targetId];
|
||||||
|
}
|
||||||
|
if (targetType === "entity") {
|
||||||
|
return !!this.states[targetId];
|
||||||
|
}
|
||||||
|
if (targetType === "label") {
|
||||||
|
return !!this._getLabel(targetId);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderTargetBadge(
|
||||||
|
icon: TemplateResult | typeof nothing,
|
||||||
|
label: string,
|
||||||
|
alert = false
|
||||||
|
) {
|
||||||
|
return html`<div class="target ${alert ? "alert" : ""}">
|
||||||
|
${icon}
|
||||||
|
<div class="label">${label}</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _loadConfigEntries() {
|
||||||
|
const configEntries = await getConfigEntries(this.hass);
|
||||||
|
this._configEntryLookup = Object.fromEntries(
|
||||||
|
configEntries.map((entry) => [entry.entry_id, entry])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderTarget(
|
||||||
|
targetType: "floor" | "area" | "device" | "entity" | "label",
|
||||||
|
targetId: string
|
||||||
|
) {
|
||||||
|
if (targetType === "entity" && ["all", "none"].includes(targetId)) {
|
||||||
|
return this._renderTargetBadge(
|
||||||
|
html`<ha-svg-icon .path=${mdiShape}></ha-svg-icon>`,
|
||||||
|
this.localize(
|
||||||
|
`ui.panel.config.automation.editor.target_summary.${targetId as "all" | "none"}_entities`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = this._checkTargetExists(targetType, targetId);
|
||||||
|
if (!exists) {
|
||||||
|
return this._renderTargetBadge(
|
||||||
|
html`<ha-svg-icon .path=${mdiAlert}></ha-svg-icon>`,
|
||||||
|
getTargetText(this.hass, targetType, targetId, this._getLabel),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetType === "device" && !this._configEntryLookup) {
|
||||||
|
const loadConfigEntries = this._loadConfigEntries().then(() =>
|
||||||
|
this._renderTargetBadge(
|
||||||
|
getTargetIcon(
|
||||||
|
this.hass,
|
||||||
|
targetType,
|
||||||
|
targetId,
|
||||||
|
this._configEntryLookup!
|
||||||
|
),
|
||||||
|
getTargetText(this.hass, targetType, targetId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return html`${until(loadConfigEntries, nothing)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._renderTargetBadge(
|
||||||
|
getTargetIcon(
|
||||||
|
this.hass,
|
||||||
|
targetType,
|
||||||
|
targetId,
|
||||||
|
this._configEntryLookup || {},
|
||||||
|
this._getLabel
|
||||||
|
),
|
||||||
|
getTargetText(this.hass, targetType, targetId, this._getLabel)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
display: contents;
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
.target-wrapper {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: var(--ha-space-1);
|
||||||
|
}
|
||||||
|
.target {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: var(--ha-space-1);
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: var(--ha-border-radius-md);
|
||||||
|
background: var(--ha-color-fill-neutral-normal-resting);
|
||||||
|
padding: 0 var(--ha-space-2) 0 var(--ha-space-1);
|
||||||
|
color: var(--ha-color-on-neutral-normal);
|
||||||
|
overflow: hidden;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
.target.alert {
|
||||||
|
background: var(--ha-color-fill-warning-normal-resting);
|
||||||
|
color: var(--ha-color-on-warning-normal);
|
||||||
|
}
|
||||||
|
.target .label {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.target ha-icon,
|
||||||
|
.target ha-svg-icon,
|
||||||
|
.target ha-domain-icon {
|
||||||
|
display: flex;
|
||||||
|
padding: var(--ha-space-1) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.target ha-floor-icon {
|
||||||
|
display: flex;
|
||||||
|
height: 32px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-row-targets": HaAutomationRowTargets;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,10 @@ import {
|
|||||||
mdiRenameBox,
|
mdiRenameBox,
|
||||||
mdiStopCircleOutline,
|
mdiStopCircleOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import type {
|
||||||
|
HassServiceTarget,
|
||||||
|
UnsubscribeFunc,
|
||||||
|
} from "home-assistant-js-websocket";
|
||||||
import { dump } from "js-yaml";
|
import { dump } from "js-yaml";
|
||||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
@@ -44,6 +47,7 @@ import "../../../../components/ha-svg-icon";
|
|||||||
import { TRIGGER_ICONS } from "../../../../components/ha-trigger-icon";
|
import { TRIGGER_ICONS } from "../../../../components/ha-trigger-icon";
|
||||||
import type {
|
import type {
|
||||||
AutomationClipboard,
|
AutomationClipboard,
|
||||||
|
PlatformTrigger,
|
||||||
Trigger,
|
Trigger,
|
||||||
TriggerList,
|
TriggerList,
|
||||||
TriggerSidebarConfig,
|
TriggerSidebarConfig,
|
||||||
@@ -64,6 +68,7 @@ import { isMac } from "../../../../util/is_mac";
|
|||||||
import { showToast } from "../../../../util/toast";
|
import { showToast } from "../../../../util/toast";
|
||||||
import "../ha-automation-editor-warning";
|
import "../ha-automation-editor-warning";
|
||||||
import { overflowStyles, rowStyles } from "../styles";
|
import { overflowStyles, rowStyles } from "../styles";
|
||||||
|
import "../target/ha-automation-row-targets";
|
||||||
import "./ha-automation-trigger-editor";
|
import "./ha-automation-trigger-editor";
|
||||||
import type HaAutomationTriggerEditor from "./ha-automation-trigger-editor";
|
import type HaAutomationTriggerEditor from "./ha-automation-trigger-editor";
|
||||||
import "./types/ha-automation-trigger-calendar";
|
import "./types/ha-automation-trigger-calendar";
|
||||||
@@ -205,6 +210,11 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
></ha-trigger-icon>`}
|
></ha-trigger-icon>`}
|
||||||
<h3 slot="header">
|
<h3 slot="header">
|
||||||
${describeTrigger(this.trigger, this.hass, this._entityReg)}
|
${describeTrigger(this.trigger, this.hass, this._entityReg)}
|
||||||
|
${type === "platform" &&
|
||||||
|
"target" in
|
||||||
|
this.triggerDescriptions[(this.trigger as PlatformTrigger).trigger]
|
||||||
|
? this._renderTargets((this.trigger as PlatformTrigger).target)
|
||||||
|
: nothing}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<slot name="icons" slot="icons"></slot>
|
<slot name="icons" slot="icons"></slot>
|
||||||
@@ -450,6 +460,14 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderTargets = memoizeOne(
|
||||||
|
(target?: HassServiceTarget) =>
|
||||||
|
html`<ha-automation-row-targets
|
||||||
|
.hass=${this.hass}
|
||||||
|
.target=${target}
|
||||||
|
></ha-automation-row-targets>`
|
||||||
|
);
|
||||||
|
|
||||||
protected willUpdate(changedProperties) {
|
protected willUpdate(changedProperties) {
|
||||||
// on yaml toggle --> clear warnings
|
// on yaml toggle --> clear warnings
|
||||||
if (changedProperties.has("yamlMode")) {
|
if (changedProperties.has("yamlMode")) {
|
||||||
|
|||||||
@@ -4077,6 +4077,13 @@
|
|||||||
"services": "Services",
|
"services": "Services",
|
||||||
"helpers": "Helpers",
|
"helpers": "Helpers",
|
||||||
"entity_hidden": "[%key:ui::panel::config::devices::entities::hidden%]",
|
"entity_hidden": "[%key:ui::panel::config::devices::entities::hidden%]",
|
||||||
|
"target_summary": {
|
||||||
|
"no_target": "No target set",
|
||||||
|
"targets": "{count} {count, plural,\n one {target}\n other {targets}\n}",
|
||||||
|
"invalid": "Invalid target",
|
||||||
|
"all_entities": "All entities",
|
||||||
|
"none_entities": "No entities"
|
||||||
|
},
|
||||||
"triggers": {
|
"triggers": {
|
||||||
"name": "Triggers",
|
"name": "Triggers",
|
||||||
"header": "When",
|
"header": "When",
|
||||||
@@ -4588,11 +4595,11 @@
|
|||||||
"service": "Perform an action",
|
"service": "Perform an action",
|
||||||
"target_template": "templated {name}",
|
"target_template": "templated {name}",
|
||||||
"target_every_entity": "every entity",
|
"target_every_entity": "every entity",
|
||||||
"target_unknown_entity": "unknown entity",
|
"target_unknown_entity": "Unknown entity",
|
||||||
"target_unknown_device": "unknown device",
|
"target_unknown_device": "Unknown device",
|
||||||
"target_unknown_area": "unknown area",
|
"target_unknown_area": "Unknown area",
|
||||||
"target_unknown_floor": "unknown floor",
|
"target_unknown_floor": "Unknown floor",
|
||||||
"target_unknown_label": "unknown label"
|
"target_unknown_label": "Unknown label"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"play_media": {
|
"play_media": {
|
||||||
|
|||||||
Reference in New Issue
Block a user