mirror of
https://github.com/home-assistant/frontend.git
synced 2026-05-29 12:31:52 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73e05237a4 | |||
| 6137d7ffff | |||
| 625324846f |
+1
-1
@@ -86,7 +86,7 @@
|
||||
"core-js": "3.49.0",
|
||||
"cropperjs": "1.6.2",
|
||||
"culori": "4.0.2",
|
||||
"date-fns": "4.2.1",
|
||||
"date-fns": "4.2.0",
|
||||
"deep-clone-simple": "1.1.1",
|
||||
"deep-freeze": "0.0.1",
|
||||
"dialog-polyfill": "0.5.6",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { createContext } from "@lit/context";
|
||||
import type {
|
||||
Connection,
|
||||
HassEntityAttributeBase,
|
||||
@@ -491,12 +490,12 @@ export const migrateAutomationTrigger = (
|
||||
|
||||
export const flattenTriggers = (
|
||||
triggers: undefined | Trigger | Trigger[]
|
||||
): Exclude<Trigger, TriggerList>[] => {
|
||||
): Trigger[] => {
|
||||
if (!triggers) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const flatTriggers: Exclude<Trigger, TriggerList>[] = [];
|
||||
const flatTriggers: Trigger[] = [];
|
||||
|
||||
ensureArray(triggers).forEach((t) => {
|
||||
if ("triggers" in t) {
|
||||
@@ -615,7 +614,6 @@ export interface BaseSidebarConfig {
|
||||
|
||||
export interface TriggerSidebarConfig extends BaseSidebarConfig {
|
||||
save: (value: Trigger) => void;
|
||||
editId: () => void;
|
||||
rename: () => void;
|
||||
disable: () => void;
|
||||
duplicate: () => void;
|
||||
@@ -699,7 +697,3 @@ export interface ShowAutomationEditorParams {
|
||||
data?: Partial<AutomationConfig>;
|
||||
expanded?: boolean;
|
||||
}
|
||||
|
||||
export const automationConfigContext = createContext<
|
||||
AutomationConfig | undefined
|
||||
>("automationConfig");
|
||||
|
||||
@@ -27,7 +27,6 @@ import type {
|
||||
LegacyTrigger,
|
||||
Trigger,
|
||||
} from "./automation";
|
||||
import { flattenTriggers } from "./automation";
|
||||
import { getConditionDomain, getConditionObjectId } from "./condition";
|
||||
import type {
|
||||
DeviceCondition,
|
||||
@@ -108,36 +107,6 @@ const formatNumericLimitValue = (
|
||||
: value;
|
||||
};
|
||||
|
||||
export interface TriggerInfo {
|
||||
id: string;
|
||||
label: string;
|
||||
triggerType: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export const getTriggerInfos = (
|
||||
triggers: Trigger[] | undefined,
|
||||
hass: HomeAssistant,
|
||||
entityRegistry: EntityRegistryEntry[]
|
||||
): TriggerInfo[] => {
|
||||
if (!triggers) {
|
||||
return [];
|
||||
}
|
||||
const map = new Map<string, TriggerInfo>();
|
||||
for (const t of flattenTriggers(triggers)) {
|
||||
if (isTriggerList(t) || !t.id || map.get(t.id)) {
|
||||
continue;
|
||||
}
|
||||
map.set(t.id, {
|
||||
id: t.id,
|
||||
label: describeTrigger(t, hass, entityRegistry),
|
||||
triggerType: t.trigger,
|
||||
count: 1,
|
||||
});
|
||||
}
|
||||
return Array.from(map.values());
|
||||
};
|
||||
|
||||
export const describeTrigger = (
|
||||
trigger: Trigger,
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -2,23 +2,16 @@ import { computeAreaName } from "../../common/entity/compute_area_name";
|
||||
import { computeDeviceNameDisplay } from "../../common/entity/compute_device_name";
|
||||
import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { getDeviceArea } from "../../common/entity/context/get_device_context";
|
||||
import type { LocalizeFunc } from "../../common/translations/localize";
|
||||
import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "../../components/device/ha-device-picker";
|
||||
import type { PickerComboBoxItem } from "../../components/ha-picker-combo-box";
|
||||
import type { FuseWeightedKey } from "../../resources/fuseMultiTerm";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import type { ConfigEntry } from "../config_entries";
|
||||
import type { HaEntityPickerEntityFilterFunc } from "../entity/entity";
|
||||
import type {
|
||||
EntityRegistryDisplayEntry,
|
||||
EntityRegistryEntry,
|
||||
} from "../entity/entity_registry";
|
||||
import { domainToName } from "../integration";
|
||||
import {
|
||||
getDeviceEntityDisplayLookup,
|
||||
type DeviceEntityDisplayLookup,
|
||||
type DeviceRegistryEntry,
|
||||
} from "./device_registry";
|
||||
|
||||
export interface DevicePickerItem extends PickerComboBoxItem {
|
||||
@@ -26,46 +19,6 @@ export interface DevicePickerItem extends PickerComboBoxItem {
|
||||
domain_name?: string;
|
||||
}
|
||||
|
||||
export interface DeviceAreaLabel {
|
||||
areaName?: string;
|
||||
viaDeviceName?: string;
|
||||
viaDeviceAreaName?: string;
|
||||
}
|
||||
|
||||
export const computeDeviceAreaLabel = (
|
||||
device: DeviceRegistryEntry,
|
||||
areas: HomeAssistant["areas"],
|
||||
devices: HomeAssistant["devices"],
|
||||
states: HomeAssistant["states"],
|
||||
localize: LocalizeFunc,
|
||||
language: HomeAssistant["language"],
|
||||
translationMetadata: HomeAssistant["translationMetadata"],
|
||||
viaDeviceEntities?: EntityRegistryEntry[] | EntityRegistryDisplayEntry[]
|
||||
): DeviceAreaLabel => {
|
||||
const area = getDeviceArea(device, areas);
|
||||
|
||||
const viaDevice = device.via_device_id
|
||||
? devices[device.via_device_id]
|
||||
: undefined;
|
||||
const viaDeviceName = viaDevice
|
||||
? computeDeviceNameDisplay(viaDevice, localize, states, viaDeviceEntities)
|
||||
: undefined;
|
||||
const viaDeviceArea = viaDevice ? getDeviceArea(viaDevice, areas) : undefined;
|
||||
const viaDeviceAreaName = viaDeviceArea
|
||||
? computeAreaName(viaDeviceArea)
|
||||
: undefined;
|
||||
|
||||
const isRTL = computeRTL(language, translationMetadata.translations);
|
||||
|
||||
const areaName = area
|
||||
? computeAreaName(area)
|
||||
: viaDeviceAreaName
|
||||
? `${viaDeviceAreaName}${isRTL ? " ◂ " : " ▸ "}${viaDeviceName}`
|
||||
: viaDeviceName || undefined;
|
||||
|
||||
return { areaName, viaDeviceName, viaDeviceAreaName };
|
||||
};
|
||||
|
||||
export const deviceComboBoxKeys: FuseWeightedKey[] = [
|
||||
{
|
||||
name: "search_labels.deviceName",
|
||||
@@ -83,14 +36,6 @@ export const deviceComboBoxKeys: FuseWeightedKey[] = [
|
||||
name: "search_labels.domain",
|
||||
weight: 4,
|
||||
},
|
||||
{
|
||||
name: "search_labels.viaDeviceName",
|
||||
weight: 3,
|
||||
},
|
||||
{
|
||||
name: "search_labels.viaDeviceArea",
|
||||
weight: 3,
|
||||
},
|
||||
];
|
||||
|
||||
export const getDevices = (
|
||||
@@ -204,19 +149,9 @@ export const getDevices = (
|
||||
deviceEntityLookup[device.id]
|
||||
);
|
||||
|
||||
const { areaName, viaDeviceName, viaDeviceAreaName } =
|
||||
computeDeviceAreaLabel(
|
||||
device,
|
||||
hass.areas,
|
||||
hass.devices,
|
||||
hass.states,
|
||||
hass.localize,
|
||||
hass.language,
|
||||
hass.translationMetadata,
|
||||
device.via_device_id
|
||||
? deviceEntityLookup[device.via_device_id]
|
||||
: undefined
|
||||
);
|
||||
const area = getDeviceArea(device, hass.areas);
|
||||
|
||||
const areaName = area ? computeAreaName(area) : undefined;
|
||||
|
||||
const configEntry = device.primary_config_entry
|
||||
? configEntryLookup?.[device.primary_config_entry]
|
||||
@@ -239,8 +174,6 @@ export const getDevices = (
|
||||
areaName: areaName || null,
|
||||
domain: domain || null,
|
||||
domainName: domainName || null,
|
||||
viaDeviceName: viaDeviceName || null,
|
||||
viaDeviceArea: viaDeviceAreaName || null,
|
||||
},
|
||||
sorting_label: [primary, areaName, domainName].filter(Boolean).join("_"),
|
||||
};
|
||||
|
||||
@@ -8,7 +8,6 @@ import type {
|
||||
Trigger,
|
||||
TriggerList,
|
||||
} from "./automation";
|
||||
import { flattenTriggers } from "./automation";
|
||||
import type { Selector, TargetSelector } from "./selector";
|
||||
|
||||
export const TRIGGER_COLLECTIONS: AutomationElementGroupCollection[] = [
|
||||
@@ -57,11 +56,6 @@ export const TRIGGER_COLLECTIONS: AutomationElementGroupCollection[] = [
|
||||
export const isTriggerList = (trigger: Trigger): trigger is TriggerList =>
|
||||
"triggers" in trigger;
|
||||
|
||||
export const getTriggerIds = (triggers: Trigger[]): string[] =>
|
||||
flattenTriggers(triggers)
|
||||
.map((trigger) => trigger.id)
|
||||
.filter((id): id is string => !!id);
|
||||
|
||||
export interface TriggerDescription {
|
||||
target?: TargetSelector["target"];
|
||||
fields: Record<
|
||||
|
||||
@@ -51,25 +51,59 @@ interface ActionDefinition {
|
||||
icon: string;
|
||||
}
|
||||
|
||||
const ADD_TO_TARGET_PLACEHOLDER = "__HA_ADD_TO_TARGET__";
|
||||
|
||||
export const ADD_TO_ACTION_ICONS: Record<AddToActionKey, string> = {
|
||||
automation_trigger: "mdi:robot-outline",
|
||||
automation_condition: "mdi:playlist-check",
|
||||
automation_action: "mdi:play-circle-outline",
|
||||
script_action: "mdi:script-text-outline",
|
||||
scene: "mdi:palette",
|
||||
};
|
||||
|
||||
export const DEFAULT_ACTION_DEFS: ActionDefinition[] = [
|
||||
{
|
||||
translation_key: "automation_trigger",
|
||||
icon: "mdi:robot-outline",
|
||||
icon: ADD_TO_ACTION_ICONS.automation_trigger,
|
||||
},
|
||||
{
|
||||
translation_key: "automation_condition",
|
||||
icon: "mdi:playlist-check",
|
||||
icon: ADD_TO_ACTION_ICONS.automation_condition,
|
||||
},
|
||||
{
|
||||
translation_key: "automation_action",
|
||||
icon: "mdi:play-circle-outline",
|
||||
icon: ADD_TO_ACTION_ICONS.automation_action,
|
||||
},
|
||||
{
|
||||
translation_key: "script_action",
|
||||
icon: "mdi:script-text-outline",
|
||||
icon: ADD_TO_ACTION_ICONS.script_action,
|
||||
},
|
||||
];
|
||||
|
||||
export const getAddToActionLabel = (
|
||||
localize: LocalizeFunc,
|
||||
key: AddToActionKey,
|
||||
target: string
|
||||
): string =>
|
||||
localize(`ui.dialogs.more_info_control.add_to.actions.${key}`, { target });
|
||||
|
||||
export const getAddToActionLabelParts = (
|
||||
localize: LocalizeFunc,
|
||||
key: AddToActionKey
|
||||
): [string, string] => {
|
||||
const label = getAddToActionLabel(localize, key, ADD_TO_TARGET_PLACEHOLDER);
|
||||
const placeholderIndex = label.indexOf(ADD_TO_TARGET_PLACEHOLDER);
|
||||
|
||||
if (placeholderIndex === -1) {
|
||||
return [label, ""];
|
||||
}
|
||||
|
||||
return [
|
||||
label.slice(0, placeholderIndex),
|
||||
label.slice(placeholderIndex + ADD_TO_TARGET_PLACEHOLDER.length),
|
||||
];
|
||||
};
|
||||
|
||||
export const getDefaultAddToActions = (
|
||||
states: HomeAssistant["states"],
|
||||
localize: LocalizeFunc,
|
||||
@@ -81,14 +115,12 @@ export const getDefaultAddToActions = (
|
||||
type: "default",
|
||||
key: def.translation_key,
|
||||
enabled: true,
|
||||
name: localize(
|
||||
`ui.dialogs.more_info_control.add_to.actions.${def.translation_key}`,
|
||||
{
|
||||
target:
|
||||
states[entityId] !== undefined
|
||||
? formatEntityName(states[entityId], undefined)
|
||||
: entityId,
|
||||
}
|
||||
name: getAddToActionLabel(
|
||||
localize,
|
||||
def.translation_key,
|
||||
states[entityId] !== undefined
|
||||
? formatEntityName(states[entityId], undefined)
|
||||
: entityId
|
||||
),
|
||||
icon: def.icon,
|
||||
})
|
||||
|
||||
@@ -5,17 +5,17 @@ import "../../components/ha-icon";
|
||||
import "../../components/ha-spinner";
|
||||
import "../../components/item/ha-list-item-button";
|
||||
import "../../components/list/ha-list-base";
|
||||
import type { HaListItemButton } from "../../components/item/ha-list-item-button";
|
||||
import "../../panels/config/automation/target/ha-automation-target-badge";
|
||||
import { showToast } from "../../util/toast";
|
||||
|
||||
import type { HASSDomCurrentTargetEvent } from "../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import {
|
||||
type EntityAddToAction,
|
||||
type AddToActionKey,
|
||||
type EntityAddToActions,
|
||||
addToActionHandler,
|
||||
getDefaultAddToActions,
|
||||
getAddToActionLabelParts,
|
||||
} from "./add-to";
|
||||
|
||||
@customElement("ha-more-info-add-to")
|
||||
@@ -65,14 +65,18 @@ export class HaMoreInfoAddTo extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private async _actionSelected(
|
||||
ev: HASSDomCurrentTargetEvent<
|
||||
HaListItemButton & {
|
||||
action: EntityAddToAction;
|
||||
}
|
||||
>
|
||||
) {
|
||||
const action = ev.currentTarget.action;
|
||||
private async _actionSelected(ev: Event) {
|
||||
const item = ev.currentTarget as HTMLElement;
|
||||
const actions =
|
||||
item.dataset.actionSource === "external"
|
||||
? this._externalActions
|
||||
: this._defaultActions;
|
||||
const action = actions[Number(item.dataset.actionIndex)];
|
||||
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!action.enabled) {
|
||||
return;
|
||||
}
|
||||
@@ -110,16 +114,25 @@ export class HaMoreInfoAddTo extends LitElement {
|
||||
addToActionHandler(action.key, { entity_id: this.entityId });
|
||||
}
|
||||
|
||||
private _renderActionItems(actions: EntityAddToActions) {
|
||||
private _renderActionItems(
|
||||
actions: EntityAddToActions,
|
||||
source: "default" | "external"
|
||||
) {
|
||||
return actions.map(
|
||||
(action) => html`
|
||||
(action, index) => html`
|
||||
<ha-list-item-button
|
||||
aria-label=${action.name}
|
||||
data-action-index=${index}
|
||||
data-action-source=${source}
|
||||
.disabled=${!action.enabled}
|
||||
.action=${action}
|
||||
@click=${this._actionSelected}
|
||||
>
|
||||
<ha-icon slot="start" .icon=${action.icon}></ha-icon>
|
||||
<span slot="headline">${action.name}</span>
|
||||
<span slot="headline" class="action-label">
|
||||
${action.type === "default"
|
||||
? this._renderDefaultActionLabel(action.key)
|
||||
: action.name}
|
||||
</span>
|
||||
${action.description
|
||||
? html`<span slot="supporting-text">${action.description}</span>`
|
||||
: nothing}
|
||||
@@ -128,6 +141,30 @@ export class HaMoreInfoAddTo extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _renderDefaultActionLabel(key: AddToActionKey) {
|
||||
const [beforeTarget, afterTarget] = getAddToActionLabelParts(
|
||||
this.hass.localize,
|
||||
key
|
||||
);
|
||||
|
||||
return html`${beforeTarget}${this._renderTarget()}${afterTarget}`;
|
||||
}
|
||||
|
||||
private _renderTarget() {
|
||||
return html`<ha-automation-target-badge
|
||||
target-type="entity"
|
||||
.targetId=${this.entityId}
|
||||
.label=${this._targetLabel}
|
||||
></ha-automation-target-badge>`;
|
||||
}
|
||||
|
||||
private get _targetLabel(): string {
|
||||
const stateObj = this.hass.states[this.entityId];
|
||||
return stateObj
|
||||
? this.hass.formatEntityName(stateObj, undefined)
|
||||
: this.entityId;
|
||||
}
|
||||
|
||||
protected async firstUpdated() {
|
||||
await this._loadActions();
|
||||
this._loading = false;
|
||||
@@ -154,7 +191,7 @@ export class HaMoreInfoAddTo extends LitElement {
|
||||
|
||||
return html`
|
||||
<ha-list-base>
|
||||
${this._renderActionItems(this._defaultActions)}
|
||||
${this._renderActionItems(this._defaultActions, "default")}
|
||||
</ha-list-base>
|
||||
${this._externalActions.length
|
||||
? html`
|
||||
@@ -164,7 +201,7 @@ export class HaMoreInfoAddTo extends LitElement {
|
||||
)}
|
||||
</h2>
|
||||
<ha-list-base>
|
||||
${this._renderActionItems(this._externalActions)}
|
||||
${this._renderActionItems(this._externalActions, "external")}
|
||||
</ha-list-base>
|
||||
`
|
||||
: nothing}
|
||||
@@ -197,6 +234,13 @@ export class HaMoreInfoAddTo extends LitElement {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.action-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-1);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
} from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
@@ -21,7 +20,7 @@ import type { LabelRegistryEntry } from "../../../../data/label/label_registry";
|
||||
import { haStyleScrollbar } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { AddAutomationElementListItem } from "../add-automation-element-dialog";
|
||||
import { getTargetIcon } from "../target/get_target_icon";
|
||||
import "../target/ha-automation-target-badge";
|
||||
|
||||
type Target = [string, string | undefined, string | undefined];
|
||||
|
||||
@@ -108,7 +107,10 @@ export class HaAutomationAddItems extends LitElement {
|
||||
items,
|
||||
(item) => item.key,
|
||||
(item) => html`
|
||||
<ha-list-item-button .value=${item.key} @click=${this._selected}>
|
||||
<ha-list-item-button
|
||||
data-value=${item.key}
|
||||
@click=${this._selected}
|
||||
>
|
||||
<div slot="headline" class=${this.target ? "item-headline" : ""}>
|
||||
${item.name}${this._renderTarget(this.target)}
|
||||
</div>
|
||||
@@ -154,33 +156,22 @@ export class HaAutomationAddItems extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderTarget = memoizeOne((target?: Target) => {
|
||||
private _renderTarget(target?: Target) {
|
||||
if (!target) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`<div class="selected-target">
|
||||
${getTargetIcon(
|
||||
{
|
||||
entities: this.hass.entities,
|
||||
devices: this.hass.devices,
|
||||
areas: this.hass.areas,
|
||||
floors: this.hass.floors,
|
||||
},
|
||||
this.hass.states,
|
||||
target[0],
|
||||
target[1],
|
||||
this.configEntryLookup,
|
||||
this.getLabel
|
||||
)}
|
||||
<div class="label">${target[2]}</div>
|
||||
</div>`;
|
||||
});
|
||||
return html`<ha-automation-target-badge
|
||||
.targetType=${target[0]}
|
||||
.targetId=${target[1]}
|
||||
.label=${target[2]}
|
||||
></ha-automation-target-badge>`;
|
||||
}
|
||||
|
||||
private _selected(ev) {
|
||||
const item = ev.currentTarget;
|
||||
const item = ev.currentTarget as HTMLElement;
|
||||
fireEvent(this, "value-changed", {
|
||||
value: item.value,
|
||||
value: item.dataset.value,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -301,42 +292,6 @@ export class HaAutomationAddItems extends LitElement {
|
||||
ha-svg-icon.plus {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.selected-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);
|
||||
border: var(--ha-border-width-sm) solid
|
||||
var(--ha-color-border-neutral-quiet);
|
||||
color: var(--ha-color-on-neutral-normal);
|
||||
overflow: hidden;
|
||||
}
|
||||
.selected-target .label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.selected-target ha-icon,
|
||||
.selected-target ha-svg-icon,
|
||||
.selected-target ha-domain-icon {
|
||||
display: flex;
|
||||
padding: var(--ha-space-1) 0;
|
||||
}
|
||||
|
||||
.selected-target ha-floor-icon {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
align-items: center;
|
||||
}
|
||||
.selected-target ha-domain-icon {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import type {
|
||||
} from "home-assistant-js-websocket";
|
||||
import { dump } from "js-yaml";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
@@ -44,7 +44,6 @@ import type { HaAutomationRow } from "../../../../components/automation/ha-autom
|
||||
import "../../../../components/automation/ha-automation-row-event-chip";
|
||||
import "../../../../components/automation/ha-automation-row-live-test";
|
||||
import type { LiveTestState } from "../../../../components/automation/ha-automation-row-live-test";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-condition-icon";
|
||||
import "../../../../components/ha-dropdown";
|
||||
@@ -52,26 +51,18 @@ import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
|
||||
import "../../../../components/ha-dropdown-item";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-tooltip";
|
||||
import "../../../../components/ha-trigger-icon";
|
||||
import type {
|
||||
AutomationClipboard,
|
||||
AutomationConfig,
|
||||
Condition,
|
||||
ConditionSidebarConfig,
|
||||
PlatformCondition,
|
||||
TriggerCondition,
|
||||
} from "../../../../data/automation";
|
||||
import {
|
||||
automationConfigContext,
|
||||
isCondition,
|
||||
subscribeCondition,
|
||||
testCondition,
|
||||
} from "../../../../data/automation";
|
||||
import {
|
||||
describeCondition,
|
||||
getTriggerInfos,
|
||||
} from "../../../../data/automation_i18n";
|
||||
import { describeCondition } from "../../../../data/automation_i18n";
|
||||
import type { ConditionDescriptions } from "../../../../data/condition";
|
||||
import { CONDITION_BUILDING_BLOCKS } from "../../../../data/condition";
|
||||
import {
|
||||
@@ -91,7 +82,6 @@ import type { HomeAssistant } from "../../../../types";
|
||||
import { isMac } from "../../../../util/is_mac";
|
||||
import { showEditorToast } from "../editor-toast";
|
||||
import "../ha-automation-editor-warning";
|
||||
import "../ha-trigger-id-chip";
|
||||
import { overflowStyles, rowStyles } from "../styles";
|
||||
import "../target/ha-automation-row-targets";
|
||||
import "./ha-automation-condition-editor";
|
||||
@@ -165,10 +155,6 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
|
||||
@state() private _liveTestResult: LiveTestState = "unknown";
|
||||
|
||||
@state()
|
||||
@consume({ context: automationConfigContext, subscribe: true })
|
||||
private _automationConfig?: AutomationConfig;
|
||||
|
||||
@state()
|
||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||
_entityReg: EntityRegistryEntry[] = [];
|
||||
@@ -227,13 +213,9 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
.condition=${this.condition.condition}
|
||||
></ha-condition-icon>
|
||||
<h3 slot="header">
|
||||
${this.condition.condition === "trigger"
|
||||
? this._renderTriggerConditionDescription(
|
||||
this.condition as TriggerCondition
|
||||
)
|
||||
: capitalizeFirstLetter(
|
||||
describeCondition(this.condition, this.hass, this._entityReg)
|
||||
)}
|
||||
${capitalizeFirstLetter(
|
||||
describeCondition(this.condition, this.hass, this._entityReg)
|
||||
)}
|
||||
${target !== undefined || (descriptionHasTarget && !this._isNew)
|
||||
? this._renderTargets(
|
||||
target,
|
||||
@@ -547,11 +529,9 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
>${this._renderRow()}
|
||||
<ha-automation-row-live-test
|
||||
slot="icons"
|
||||
.state=${this.condition.condition !== "trigger"
|
||||
? this._liveTestResult
|
||||
: "unknown"}
|
||||
.state=${this._liveTestResult}
|
||||
.label=${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.conditions.live_test_state.${this.condition.condition !== "trigger" ? this._liveTestResult : "unknown"}`
|
||||
`ui.panel.config.automation.editor.conditions.live_test_state.${this._liveTestResult}`
|
||||
)}
|
||||
></ha-automation-row-live-test
|
||||
></ha-automation-row>`
|
||||
@@ -584,67 +564,6 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _getTriggerInfos = memoizeOne(getTriggerInfos);
|
||||
|
||||
private _renderTriggerConditionDescription(condition: TriggerCondition) {
|
||||
const ids = ensureArray(condition.id ?? []).filter((id) => id !== "");
|
||||
const prefix = capitalizeFirstLetter(
|
||||
this.hass
|
||||
.localize(
|
||||
"ui.panel.config.automation.editor.conditions.type.trigger.description.full",
|
||||
{ id: "" }
|
||||
)
|
||||
.trim()
|
||||
);
|
||||
if (!ids.length) {
|
||||
return html`${prefix}
|
||||
<div class="trigger warning">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.type.trigger.description.no_trigger"
|
||||
)}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const triggers = ensureArray(this._automationConfig?.triggers || []);
|
||||
|
||||
const triggerInfos = this._getTriggerInfos(
|
||||
triggers,
|
||||
this.hass,
|
||||
this._entityReg
|
||||
);
|
||||
const infoById = new Map(triggerInfos.map((info) => [info.id, info]));
|
||||
return html`${prefix}
|
||||
${ids
|
||||
.filter((id) => infoById.get(id))
|
||||
.map((id) => {
|
||||
const info = infoById.get(id)!;
|
||||
|
||||
const triggerIcon = html`<ha-trigger-icon
|
||||
.slot=${ids.length < 4 ? "start" : ""}
|
||||
.hass=${this.hass}
|
||||
.trigger=${info.triggerType}
|
||||
></ha-trigger-icon>`;
|
||||
|
||||
return html`
|
||||
<div class="trigger">
|
||||
${ids.length < 4 ? triggerIcon : nothing}
|
||||
<ha-trigger-id-chip id=${`trigger-${id}`} .triggerId=${id}>
|
||||
</ha-trigger-id-chip>
|
||||
${ids.length < 4
|
||||
? html`<span>${info.label}</span>`
|
||||
: html`<ha-tooltip .for=${`trigger-${id}`}></ha-tooltip>`}
|
||||
${ids.length >= 4
|
||||
? html`<ha-tooltip .for=${`trigger-${id}`}>
|
||||
${ids.length >= 4
|
||||
? html`<div>${triggerIcon}${info.label}</div>`
|
||||
: nothing}
|
||||
</ha-tooltip>`
|
||||
: nothing}
|
||||
</div>
|
||||
`;
|
||||
})}`;
|
||||
}
|
||||
|
||||
private _renderTargets = memoizeOne(
|
||||
(
|
||||
target?: HassServiceTarget,
|
||||
@@ -1188,26 +1107,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
rowStyles,
|
||||
overflowStyles,
|
||||
css`
|
||||
.trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-2);
|
||||
background-color: var(--ha-color-fill-neutral-normal-resting);
|
||||
border-radius: var(--ha-border-radius-md);
|
||||
padding-inline: var(--ha-space-2);
|
||||
color: var(--ha-color-on-neutral-normal);
|
||||
height: 32px;
|
||||
}
|
||||
.trigger.warning {
|
||||
background-color: var(--ha-color-fill-warning-normal-resting);
|
||||
color: var(--ha-color-on-warning-normal);
|
||||
}
|
||||
`,
|
||||
];
|
||||
return [rowStyles, overflowStyles];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { ensureArray } from "../../../../../common/array/ensure-array";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-alert";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/item/ha-list-item-option";
|
||||
import type { HaListItemOption } from "../../../../../components/item/ha-list-item-option";
|
||||
import "../../../../../components/list/ha-list-selectable";
|
||||
import type { HaListSelectable } from "../../../../../components/list/ha-list-selectable";
|
||||
import type { HaListSelectedDetail } from "../../../../../components/list/types";
|
||||
import {
|
||||
automationConfigContext,
|
||||
flattenTriggers,
|
||||
type AutomationConfig,
|
||||
type Trigger,
|
||||
type TriggerCondition,
|
||||
} from "../../../../../data/automation";
|
||||
import {
|
||||
getTriggerInfos,
|
||||
type TriggerInfo,
|
||||
} from "../../../../../data/automation_i18n";
|
||||
import { fullEntitiesContext } from "../../../../../data/context";
|
||||
import type { EntityRegistryEntry } from "../../../../../data/entity/entity_registry";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../ha-trigger-id-chip";
|
||||
|
||||
const getTriggersIds = (triggers: Trigger[]): string[] => {
|
||||
const triggerIds = flattenTriggers(triggers)
|
||||
.map((t) => ("id" in t ? t.id : undefined))
|
||||
.filter(Boolean) as string[];
|
||||
return Array.from(new Set(triggerIds));
|
||||
};
|
||||
|
||||
@customElement("ha-automation-condition-trigger")
|
||||
export class HaTriggerCondition extends LitElement {
|
||||
@@ -34,25 +30,9 @@ export class HaTriggerCondition extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@state()
|
||||
@consume({ context: automationConfigContext, subscribe: true })
|
||||
private _automationConfig?: AutomationConfig;
|
||||
@state() private _triggerIds: string[] = [];
|
||||
|
||||
@state()
|
||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||
private _entityReg: EntityRegistryEntry[] = [];
|
||||
|
||||
private _triggerInfos = memoizeOne(
|
||||
(
|
||||
triggers: AutomationConfig["triggers"] | undefined,
|
||||
entityReg: EntityRegistryEntry[]
|
||||
): TriggerInfo[] =>
|
||||
getTriggerInfos(
|
||||
triggers ? ensureArray(triggers) : undefined,
|
||||
this.hass,
|
||||
entityReg
|
||||
)
|
||||
);
|
||||
private _unsub?: UnsubscribeFunc;
|
||||
|
||||
public static get defaultConfig(): TriggerCondition {
|
||||
return {
|
||||
@@ -61,106 +41,89 @@ export class HaTriggerCondition extends LitElement {
|
||||
};
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(triggerIds: string[]) =>
|
||||
[
|
||||
{
|
||||
name: "id",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
options: triggerIds,
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
] as const
|
||||
);
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
const details = { callback: (config) => this._automationUpdated(config) };
|
||||
fireEvent(this, "subscribe-automation-config", details);
|
||||
this._unsub = (details as any).unsub;
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this._unsub) {
|
||||
this._unsub();
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const selectedIds: (string | number)[] = ensureArray(
|
||||
this.condition.id || []
|
||||
);
|
||||
|
||||
const triggerInfos = this._triggerInfos(
|
||||
this._automationConfig?.triggers,
|
||||
this._entityReg
|
||||
);
|
||||
|
||||
if (!triggerInfos.length && !selectedIds.length) {
|
||||
return html`
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.type.trigger.no_triggers"
|
||||
)}
|
||||
</ha-alert>
|
||||
`;
|
||||
if (!this._triggerIds.length) {
|
||||
return this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.type.trigger.no_triggers"
|
||||
);
|
||||
}
|
||||
|
||||
const schema = this._schema(this._triggerIds);
|
||||
|
||||
return html`
|
||||
<ha-list-selectable @ha-list-selected=${this._valueChanged} multi>
|
||||
${this._renderOptions(selectedIds, triggerInfos)}
|
||||
</ha-list-selectable>
|
||||
<ha-form
|
||||
.schema=${schema}
|
||||
.data=${this.condition}
|
||||
.hass=${this.hass}
|
||||
.disabled=${this.disabled}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderOptions(
|
||||
selectedIds: (string | number)[],
|
||||
triggerInfos: TriggerInfo[]
|
||||
) {
|
||||
return html`
|
||||
${triggerInfos.map(
|
||||
(info) => html`
|
||||
<ha-list-item-option
|
||||
.value=${info.id}
|
||||
.selected=${selectedIds.includes(info.id)}
|
||||
appearance="checkbox"
|
||||
>
|
||||
<div class="option" slot="headline">
|
||||
<ha-trigger-id-chip
|
||||
id=${`trigger-${info.id}`}
|
||||
.triggerId=${info.id}
|
||||
>
|
||||
</ha-trigger-id-chip>
|
||||
${info.label}
|
||||
</div>
|
||||
</ha-list-item-option>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
private _computeLabelCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
): string =>
|
||||
this.hass.localize(
|
||||
`ui.panel.config.automation.editor.conditions.type.trigger.${schema.name}`
|
||||
);
|
||||
|
||||
private _automationUpdated(config?: AutomationConfig) {
|
||||
this._triggerIds = config?.triggers
|
||||
? getTriggersIds(ensureArray(config.triggers))
|
||||
: [];
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent<HaListSelectedDetail>): void {
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (
|
||||
!ev.detail.diff ||
|
||||
(!ev.detail.diff?.added.size && !ev.detail.diff?.removed.size)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const newValue = ev.detail.value;
|
||||
|
||||
const ids = ensureArray(this.condition.id || []);
|
||||
|
||||
const valueSet = ev.detail.diff.added.size
|
||||
? ev.detail.diff.added
|
||||
: ev.detail.diff.removed;
|
||||
|
||||
const index = valueSet.values().next().value;
|
||||
|
||||
if (index === undefined) {
|
||||
return;
|
||||
}
|
||||
const triggerId = (
|
||||
(ev.currentTarget as HaListSelectable).items[index] as HaListItemOption
|
||||
).value;
|
||||
if (triggerId === undefined || triggerId === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.detail.diff.added.size) {
|
||||
ids.push(triggerId);
|
||||
} else {
|
||||
const removeIndex = ids.indexOf(triggerId);
|
||||
if (removeIndex > -1) {
|
||||
ids.splice(removeIndex, 1);
|
||||
if (typeof newValue.id === "string") {
|
||||
if (!this._triggerIds.some((id) => id === newValue.id)) {
|
||||
newValue.id = "";
|
||||
}
|
||||
} else if (Array.isArray(newValue.id)) {
|
||||
newValue.id = newValue.id.filter((_id) =>
|
||||
this._triggerIds.some((id) => id === _id)
|
||||
);
|
||||
if (!newValue.id.length) {
|
||||
newValue.id = "";
|
||||
}
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", { value: { ...this.condition, id: ids } });
|
||||
fireEvent(this, "value-changed", { value: newValue });
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-1);
|
||||
color: var(--ha-color-on-neutral-normal);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
||||
import { provide } from "@lit/context";
|
||||
import {
|
||||
mdiAppleKeyboardCommand,
|
||||
mdiCog,
|
||||
@@ -21,9 +20,10 @@ import {
|
||||
mdiTransitConnection,
|
||||
mdiUndo,
|
||||
} from "@mdi/js";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { UndoRedoController } from "../../../common/controllers/undo-redo-controller";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
@@ -31,7 +31,6 @@ import { goBack, navigate } from "../../../common/navigate";
|
||||
import { promiseTimeout } from "../../../common/util/promise-timeout";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-dropdown";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
@@ -46,7 +45,6 @@ import type {
|
||||
Trigger,
|
||||
} from "../../../data/automation";
|
||||
import {
|
||||
automationConfigContext,
|
||||
deleteAutomation,
|
||||
fetchAutomationFileConfig,
|
||||
getAutomationEditorInitData,
|
||||
@@ -74,12 +72,13 @@ import { PreventUnsavedMixin } from "../../../mixins/prevent-unsaved-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { Entries, ValueChangedEvent } from "../../../types";
|
||||
import { isMac } from "../../../util/is_mac";
|
||||
import { showEditorToast } from "./editor-toast";
|
||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||
import { showAutomationModeDialog } from "./automation-mode-dialog/show-dialog-automation-mode";
|
||||
import { showAutomationSaveDialog } from "./automation-save-dialog/show-dialog-automation-save";
|
||||
import { showAutomationSaveTimeoutDialog } from "./automation-save-timeout-dialog/show-dialog-automation-save-timeout";
|
||||
import { ADD_AUTOMATION_ELEMENT_QUERY_PARAM } from "./show-add-automation-element-dialog";
|
||||
import "./blueprint-automation-editor";
|
||||
import { showEditorToast } from "./editor-toast";
|
||||
import type { EditorDomainHooks } from "./ha-automation-script-editor-mixin";
|
||||
import {
|
||||
AutomationScriptEditorMixin,
|
||||
@@ -87,7 +86,7 @@ import {
|
||||
} from "./ha-automation-script-editor-mixin";
|
||||
import "./manual-automation-editor";
|
||||
import type { HaManualAutomationEditor } from "./manual-automation-editor";
|
||||
import { ADD_AUTOMATION_ELEMENT_QUERY_PARAM } from "./show-add-automation-element-dialog";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
@@ -95,6 +94,10 @@ declare global {
|
||||
}
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"subscribe-automation-config": {
|
||||
callback: (config: AutomationConfig) => void;
|
||||
unsub?: UnsubscribeFunc;
|
||||
};
|
||||
"ui-mode-not-available": Error;
|
||||
"move-down": undefined;
|
||||
"move-up": undefined;
|
||||
@@ -122,9 +125,12 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationCo
|
||||
|
||||
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
|
||||
|
||||
@provide({ context: automationConfigContext })
|
||||
@state()
|
||||
protected config?: AutomationConfig;
|
||||
private _configSubscriptions: Record<
|
||||
string,
|
||||
(config?: AutomationConfig) => void
|
||||
> = {};
|
||||
|
||||
private _configSubscriptionsId = 1;
|
||||
|
||||
private _newAutomationId?: string;
|
||||
|
||||
@@ -398,7 +404,10 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationCo
|
||||
</ha-svg-icon>
|
||||
</ha-dropdown-item>
|
||||
</ha-dropdown>
|
||||
<div class=${this.mode === "yaml" ? "yaml-mode" : ""}>
|
||||
<div
|
||||
class=${this.mode === "yaml" ? "yaml-mode" : ""}
|
||||
@subscribe-automation-config=${this._subscribeAutomationConfig}
|
||||
>
|
||||
${this.mode === "gui"
|
||||
? html`
|
||||
<div>
|
||||
@@ -629,6 +638,12 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationCo
|
||||
) {
|
||||
this._setEntityId();
|
||||
}
|
||||
|
||||
if (changedProps.has("config")) {
|
||||
Object.values(this._configSubscriptions).forEach((sub) =>
|
||||
sub(this.config)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _setEntityId() {
|
||||
@@ -1006,6 +1021,15 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationCo
|
||||
}
|
||||
}
|
||||
|
||||
private _subscribeAutomationConfig(ev) {
|
||||
const id = this._configSubscriptionsId++;
|
||||
this._configSubscriptions[id] = ev.detail.callback;
|
||||
ev.detail.unsub = () => {
|
||||
delete this._configSubscriptions[id];
|
||||
};
|
||||
ev.detail.callback(this.config);
|
||||
}
|
||||
|
||||
protected supportedShortcuts(): SupportedShortcuts {
|
||||
return {
|
||||
s: () => this._handleSaveAutomation(),
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import { mdiPound } from "@mdi/js";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../components/ha-svg-icon";
|
||||
|
||||
/**
|
||||
* Home Assistant trigger ID chip component
|
||||
*
|
||||
* @element ha-trigger-id-chip
|
||||
* @extends {LitElement}
|
||||
*
|
||||
* @summary
|
||||
* A small chip that displays an automation trigger ID prefixed with a hash icon.
|
||||
*
|
||||
* @slot start - Optional content rendered before the hash icon (usually an icon).
|
||||
*
|
||||
* @attr {string} trigger-id - The trigger ID to display.
|
||||
* @attr {boolean} warning - Renders the chip with warning colors.
|
||||
*/
|
||||
@customElement("ha-trigger-id-chip")
|
||||
export class HaTriggerIdChip extends LitElement {
|
||||
@property({ attribute: "trigger-id" }) public triggerId!: string;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public warning = false;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<slot name="start"></slot>
|
||||
<ha-svg-icon .path=${mdiPound}></ha-svg-icon>
|
||||
<span>${this.triggerId}</span>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
background-color: var(--card-background-color);
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
border: var(--ha-border-width-sm) solid
|
||||
var(--ha-color-border-neutral-normal);
|
||||
--mdc-icon-size: 16px;
|
||||
display: inline-flex;
|
||||
gap: var(--ha-space-1);
|
||||
align-items: center;
|
||||
color: var(--ha-color-on-neutral-normal);
|
||||
padding: 0 var(--ha-space-1);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
:host([warning]) {
|
||||
border-color: var(--ha-color-border-warning-normal);
|
||||
color: var(--ha-color-on-warning-normal);
|
||||
background-color: var(--ha-color-fill-warning-quiet-resting);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-trigger-id-chip": HaTriggerIdChip;
|
||||
}
|
||||
}
|
||||
@@ -32,11 +32,11 @@ import {
|
||||
normalizeAutomationConfig,
|
||||
} from "../../../data/automation";
|
||||
import { getActionType, type Action } from "../../../data/script";
|
||||
import { showEditorToast } from "./editor-toast";
|
||||
import "./action/ha-automation-action";
|
||||
import type HaAutomationAction from "./action/ha-automation-action";
|
||||
import "./condition/ha-automation-condition";
|
||||
import type HaAutomationCondition from "./condition/ha-automation-condition";
|
||||
import { showEditorToast } from "./editor-toast";
|
||||
import { ManualEditorMixin } from "./ha-manual-editor-mixin";
|
||||
import { showPasteReplaceDialog } from "./paste-replace-dialog/show-dialog-paste-replace";
|
||||
import { manualEditorStyles, saveFabStyles } from "./styles";
|
||||
|
||||
@@ -14,15 +14,13 @@ import {
|
||||
mdiStopCircleOutline,
|
||||
} from "@mdi/js";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { keyed } from "lit/directives/keyed";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
|
||||
import "../../../../components/ha-dropdown-item";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-tooltip";
|
||||
import type {
|
||||
LegacyTrigger,
|
||||
Trigger,
|
||||
@@ -37,7 +35,6 @@ import {
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { isMac } from "../../../../util/is_mac";
|
||||
import "../ha-automation-comment";
|
||||
import "../ha-trigger-id-chip";
|
||||
import { overflowStyles, sidebarEditorStyles } from "../styles";
|
||||
import "../trigger/ha-automation-trigger-editor";
|
||||
import type HaAutomationTriggerEditor from "../trigger/ha-automation-trigger-editor";
|
||||
@@ -60,6 +57,8 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
||||
@property({ type: Number, attribute: "sidebar-key" })
|
||||
public sidebarKey?: number;
|
||||
|
||||
@state() private _requestShowId = false;
|
||||
|
||||
@state() private _warnings?: string[];
|
||||
|
||||
@query(".sidebar-editor")
|
||||
@@ -67,6 +66,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has("config")) {
|
||||
this._requestShowId = false;
|
||||
this._warnings = undefined;
|
||||
if (this.config) {
|
||||
this.yamlMode = this.config.yamlMode;
|
||||
@@ -111,21 +111,11 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
||||
@wa-select=${this._handleDropdownSelect}
|
||||
>
|
||||
<span slot="title">${title}</span>
|
||||
<div slot="subtitle" class="subtitle">
|
||||
${subtitle}
|
||||
${"id" in this.config.config
|
||||
? html`<ha-trigger-id-chip
|
||||
id="trigger-id-chip"
|
||||
.triggerId=${(
|
||||
this.config.config as Exclude<Trigger, TriggerList>
|
||||
).id}
|
||||
>
|
||||
</ha-trigger-id-chip>`
|
||||
: nothing}
|
||||
${rowDisabled
|
||||
? `(${this.hass.localize("ui.panel.config.automation.editor.actions.disabled")})`
|
||||
: nothing}
|
||||
</div>
|
||||
<span slot="subtitle"
|
||||
>${subtitle}${rowDisabled
|
||||
? ` (${this.hass.localize("ui.panel.config.automation.editor.actions.disabled")})`
|
||||
: ""}</span
|
||||
>
|
||||
<ha-dropdown-item
|
||||
slot="menu-items"
|
||||
value="rename"
|
||||
@@ -157,16 +147,18 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
||||
</div>
|
||||
</ha-dropdown-item>`
|
||||
: nothing}
|
||||
${type !== "list"
|
||||
? html` <ha-dropdown-item
|
||||
${!this.yamlMode &&
|
||||
!("id" in this.config.config) &&
|
||||
!this._requestShowId
|
||||
? html`<ha-dropdown-item
|
||||
slot="menu-items"
|
||||
value="edit_id"
|
||||
value="show_id"
|
||||
.disabled=${this.disabled || type === "list"}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiIdentifier}></ha-svg-icon>
|
||||
<div class="overflow-label">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.triggers.${"id" in this.config.config ? "edit" : "add"}_id`
|
||||
"ui.panel.config.automation.editor.triggers.edit_id"
|
||||
)}
|
||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||
</div>
|
||||
@@ -343,6 +335,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
||||
@value-changed=${this._valueChangedSidebar}
|
||||
@yaml-changed=${this._yamlChangedSidebar}
|
||||
.uiSupported=${this.config.uiSupported}
|
||||
.showId=${this._requestShowId}
|
||||
.yamlMode=${this.yamlMode}
|
||||
.disabled=${this.disabled}
|
||||
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
||||
@@ -393,6 +386,10 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
||||
fireEvent(this, "toggle-yaml-mode");
|
||||
};
|
||||
|
||||
private _showTriggerId = () => {
|
||||
this._requestShowId = true;
|
||||
};
|
||||
|
||||
private _handleDropdownSelect(ev: HaDropdownSelectEvent) {
|
||||
const action = ev.detail?.item?.value;
|
||||
|
||||
@@ -407,8 +404,8 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
||||
case "edit_comment":
|
||||
this.config.editComment();
|
||||
break;
|
||||
case "edit_id":
|
||||
this.config.editId();
|
||||
case "show_id":
|
||||
this._showTriggerId();
|
||||
break;
|
||||
case "duplicate":
|
||||
this.config.duplicate();
|
||||
@@ -434,16 +431,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static styles = [
|
||||
sidebarEditorStyles,
|
||||
overflowStyles,
|
||||
css`
|
||||
.subtitle {
|
||||
display: flex;
|
||||
gap: var(--ha-space-1);
|
||||
}
|
||||
`,
|
||||
];
|
||||
static styles = [sidebarEditorStyles, overflowStyles];
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiAlert, mdiCodeBraces, mdiShape } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing, type TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { transform } from "../../../../common/decorators/transform";
|
||||
import { isTemplate } from "../../../../common/string/has-template";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import type { ConfigEntry } from "../../../../data/config_entries";
|
||||
import {
|
||||
configEntriesContext,
|
||||
internationalizationContext,
|
||||
labelsContext,
|
||||
registriesContext,
|
||||
statesContext,
|
||||
} from "../../../../data/context";
|
||||
import type { LabelRegistryEntry } from "../../../../data/label/label_registry";
|
||||
import type { TargetType } from "../../../../data/target";
|
||||
import { getTargetIcon } from "./get_target_icon";
|
||||
import { getTargetText } from "./get_target_text";
|
||||
|
||||
const TARGET_TYPES = ["entity", "device", "area", "label", "floor"] as const;
|
||||
|
||||
const isTargetType = (targetType: string): targetType is TargetType =>
|
||||
TARGET_TYPES.includes(targetType as TargetType);
|
||||
|
||||
@customElement("ha-automation-target-badge")
|
||||
export class HaAutomationTargetBadge extends LitElement {
|
||||
@property({ attribute: "target-type" })
|
||||
public targetType!: string;
|
||||
|
||||
@property({ attribute: "target-id" })
|
||||
public targetId?: string;
|
||||
|
||||
@property()
|
||||
public label?: string;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public warning = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public error = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public interactive = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
public countTemplate: unknown = nothing;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: registriesContext, subscribe: true })
|
||||
private _registries!: ContextType<typeof registriesContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: labelsContext, subscribe: true })
|
||||
private _labelRegistry!: LabelRegistryEntry[];
|
||||
|
||||
@state()
|
||||
@consume({ context: configEntriesContext, subscribe: true })
|
||||
@transform<ConfigEntry[], Record<string, ConfigEntry>>({
|
||||
transformer: function (value) {
|
||||
return value
|
||||
? Object.fromEntries(value.map((entry) => [entry.entry_id, entry]))
|
||||
: undefined;
|
||||
},
|
||||
})
|
||||
private _configEntryLookup?: Record<string, ConfigEntry>;
|
||||
|
||||
@consume({ context: statesContext, subscribe: true })
|
||||
private _states!: ContextType<typeof statesContext>;
|
||||
|
||||
protected render() {
|
||||
const { icon, label, warning } = this._targetInfo();
|
||||
|
||||
return html`<div
|
||||
class=${classMap({
|
||||
target: true,
|
||||
warning,
|
||||
error: this.error,
|
||||
interactive: this.interactive,
|
||||
})}
|
||||
>
|
||||
${icon}
|
||||
<div class="label">${label}${this.countTemplate}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _targetInfo(): {
|
||||
icon: TemplateResult | typeof nothing;
|
||||
label: string;
|
||||
warning: boolean;
|
||||
} {
|
||||
const targetId = this.targetId;
|
||||
|
||||
if (!targetId) {
|
||||
return {
|
||||
icon: nothing,
|
||||
label: this.label || "",
|
||||
warning: this.warning,
|
||||
};
|
||||
}
|
||||
|
||||
let iconPath: string | undefined;
|
||||
let label = this.label;
|
||||
let warning = this.warning;
|
||||
|
||||
if (!isTargetType(this.targetType)) {
|
||||
return {
|
||||
icon: nothing,
|
||||
label: label || targetId,
|
||||
warning,
|
||||
};
|
||||
}
|
||||
|
||||
if (this.targetType === "entity" && ["all", "none"].includes(targetId)) {
|
||||
iconPath = mdiShape;
|
||||
label = this._i18n.localize(
|
||||
`ui.panel.config.automation.editor.target_summary.${targetId as "all" | "none"}_entities`
|
||||
);
|
||||
} else if (isTemplate(targetId)) {
|
||||
iconPath = mdiCodeBraces;
|
||||
label = this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.target_summary.template"
|
||||
);
|
||||
} else if (!this._checkTargetExists(targetId)) {
|
||||
iconPath = mdiAlert;
|
||||
label = label || this._targetText(this.targetType, targetId);
|
||||
warning = true;
|
||||
} else {
|
||||
label = label || this._targetText(this.targetType, targetId);
|
||||
}
|
||||
|
||||
const icon = iconPath
|
||||
? html`<ha-svg-icon .path=${iconPath}></ha-svg-icon>`
|
||||
: getTargetIcon(
|
||||
this._registries,
|
||||
this._states,
|
||||
this.targetType,
|
||||
targetId,
|
||||
this._configEntryLookup || {},
|
||||
this._getLabel
|
||||
);
|
||||
|
||||
return {
|
||||
icon,
|
||||
label: label || targetId,
|
||||
warning,
|
||||
};
|
||||
}
|
||||
|
||||
private _targetText(targetType: TargetType, targetId: string): string {
|
||||
return getTargetText(
|
||||
this._registries,
|
||||
this._states,
|
||||
this._i18n.localize,
|
||||
targetType,
|
||||
targetId,
|
||||
this._getLabel
|
||||
);
|
||||
}
|
||||
|
||||
private _getLabel = (id: string) =>
|
||||
this._labelRegistry?.find(({ label_id }) => label_id === id);
|
||||
|
||||
private _checkTargetExists(targetId: string): boolean {
|
||||
if (this.targetType === "floor") {
|
||||
return !!this._registries.floors[targetId];
|
||||
}
|
||||
if (this.targetType === "area") {
|
||||
return !!this._registries.areas[targetId];
|
||||
}
|
||||
if (this.targetType === "device") {
|
||||
return !!this._registries.devices[targetId];
|
||||
}
|
||||
if (this.targetType === "entity") {
|
||||
return !!this._states[targetId];
|
||||
}
|
||||
return !!this._getLabel(targetId);
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: inline-flex;
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.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);
|
||||
border: var(--ha-border-width-sm) solid
|
||||
var(--ha-color-border-neutral-quiet);
|
||||
overflow: hidden;
|
||||
height: 32px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.target.warning {
|
||||
background: var(--ha-color-fill-warning-normal-resting);
|
||||
color: var(--ha-color-on-warning-normal);
|
||||
}
|
||||
|
||||
.target.error {
|
||||
background: var(--ha-color-fill-danger-normal-resting);
|
||||
color: var(--ha-color-on-danger-normal);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.target.interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.target.interactive:hover {
|
||||
background: var(--ha-color-fill-neutral-normal-hover);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-automation-target-badge": HaAutomationTargetBadge;
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-dialog-footer";
|
||||
import "../../../../components/input/ha-input";
|
||||
import type { HaInput } from "../../../../components/input/ha-input";
|
||||
import { internationalizationContext } from "../../../../data/context";
|
||||
import { DialogMixin } from "../../../../dialogs/dialog-mixin";
|
||||
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
||||
import type { EditTriggerIdDialogParams } from "./show-edit-trigger-id";
|
||||
|
||||
@customElement("ha-automation-edit-trigger-id-dialog")
|
||||
class HaAutomationEditTriggerIdDialog extends DialogMixin<EditTriggerIdDialogParams>(
|
||||
LitElement
|
||||
) {
|
||||
@state() private _newId = "";
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
protected _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._setInitialId();
|
||||
}
|
||||
|
||||
private _setInitialId() {
|
||||
if (this.params?.id) {
|
||||
this._newId = this.params.id;
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.params) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const title = this._i18n.localize(
|
||||
`ui.panel.config.automation.editor.triggers.${
|
||||
this.params.id ? "edit_id" : "add_id"
|
||||
}`
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-dialog open header-title=${title}>
|
||||
<ha-input
|
||||
autofocus
|
||||
.label=${this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.triggers.id"
|
||||
)}
|
||||
.value=${this._newId}
|
||||
@input=${this._idChanged}
|
||||
@keydown=${this._handleKeyDown}
|
||||
></ha-input>
|
||||
<ha-alert alert-type="info">
|
||||
${this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.triggers.id_description"
|
||||
)}
|
||||
</ha-alert>
|
||||
<ha-dialog-footer slot="footer">
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
appearance="plain"
|
||||
@click=${this.closeDialog}
|
||||
>
|
||||
${this._i18n.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button slot="primaryAction" @click=${this._save}>
|
||||
${this._i18n.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
</ha-dialog-footer>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _idChanged(ev: InputEvent) {
|
||||
const target = ev.target as HaInput;
|
||||
this._newId = target.value ?? "";
|
||||
}
|
||||
|
||||
private _handleKeyDown(ev: KeyboardEvent) {
|
||||
if (ev.key === "Enter") {
|
||||
ev.preventDefault();
|
||||
this._save();
|
||||
}
|
||||
}
|
||||
|
||||
private _save(): void {
|
||||
const trimmed = this._newId.trim();
|
||||
this.params!.onUpdate(trimmed || undefined);
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-input {
|
||||
width: 100%;
|
||||
}
|
||||
ha-alert {
|
||||
display: block;
|
||||
margin-top: var(--ha-space-6);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-automation-edit-trigger-id-dialog": HaAutomationEditTriggerIdDialog;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { dynamicElement } from "../../../../common/dom/dynamic-element-directive
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-yaml-editor";
|
||||
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
|
||||
import "../../../../components/input/ha-input";
|
||||
import type { Trigger } from "../../../../data/automation";
|
||||
import { migrateAutomationTrigger } from "../../../../data/automation";
|
||||
import type { TriggerDescription } from "../../../../data/trigger";
|
||||
@@ -30,6 +31,8 @@ export default class HaAutomationTriggerEditor extends LitElement {
|
||||
|
||||
@property({ type: Boolean, attribute: "sidebar" }) public inSidebar = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "show-id" }) public showId = false;
|
||||
|
||||
@property({ attribute: false }) public description?: TriggerDescription;
|
||||
|
||||
@query("ha-yaml-editor") public yamlEditor?: HaYamlEditor;
|
||||
@@ -39,6 +42,8 @@ export default class HaAutomationTriggerEditor extends LitElement {
|
||||
|
||||
const yamlMode = this.yamlMode || !this.uiSupported;
|
||||
|
||||
const showId = "id" in this.trigger || this.showId;
|
||||
|
||||
return html`
|
||||
<div
|
||||
class=${classMap({
|
||||
@@ -72,6 +77,18 @@ export default class HaAutomationTriggerEditor extends LitElement {
|
||||
></ha-yaml-editor>
|
||||
`
|
||||
: html`
|
||||
${showId && !isTriggerList(this.trigger)
|
||||
? html`
|
||||
<ha-input
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.id"
|
||||
)}
|
||||
.value=${this.trigger.id || ""}
|
||||
.disabled=${this.disabled}
|
||||
@change=${this._idChanged}
|
||||
></ha-input>
|
||||
`
|
||||
: nothing}
|
||||
<div @value-changed=${this._onUiChanged}>
|
||||
${this.description
|
||||
? html`<ha-automation-trigger-platform
|
||||
@@ -91,6 +108,24 @@ export default class HaAutomationTriggerEditor extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _idChanged(ev: CustomEvent) {
|
||||
if (isTriggerList(this.trigger)) return;
|
||||
const newId = (ev.target as any).value;
|
||||
|
||||
if (newId === (this.trigger.id ?? "")) {
|
||||
return;
|
||||
}
|
||||
const value = { ...this.trigger };
|
||||
if (!newId) {
|
||||
delete value.id;
|
||||
} else {
|
||||
value.id = newId;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
private _onYamlChange(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
if (!ev.detail.isValid) {
|
||||
@@ -125,6 +160,9 @@ export default class HaAutomationTriggerEditor extends LitElement {
|
||||
border-top: 1px solid var(--divider-color);
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
ha-input {
|
||||
margin-bottom: var(--ha-space-3);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
mdiContentPaste,
|
||||
mdiDelete,
|
||||
mdiDotsVertical,
|
||||
mdiIdentifier,
|
||||
mdiPlayCircleOutline,
|
||||
mdiPlaylistEdit,
|
||||
mdiPlusCircleMultipleOutline,
|
||||
@@ -49,7 +48,6 @@ import "../../../../components/ha-dropdown-item";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-tooltip";
|
||||
import { TRIGGER_ICONS } from "../../../../components/ha-trigger-icon";
|
||||
import type {
|
||||
AutomationClipboard,
|
||||
@@ -75,12 +73,10 @@ import type { HomeAssistant } from "../../../../types";
|
||||
import { isMac } from "../../../../util/is_mac";
|
||||
import { showEditorToast } from "../editor-toast";
|
||||
import "../ha-automation-editor-warning";
|
||||
import "../ha-trigger-id-chip";
|
||||
import { overflowStyles, rowStyles } from "../styles";
|
||||
import "../target/ha-automation-row-targets";
|
||||
import "./ha-automation-trigger-editor";
|
||||
import type HaAutomationTriggerEditor from "./ha-automation-trigger-editor";
|
||||
import { showEditTriggerIdDialog } from "./show-edit-trigger-id";
|
||||
import "./types/ha-automation-trigger-calendar";
|
||||
import "./types/ha-automation-trigger-conversation";
|
||||
import "./types/ha-automation-trigger-device";
|
||||
@@ -248,14 +244,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
.trigger=${(this.trigger as Exclude<Trigger, TriggerList>).trigger}
|
||||
></ha-trigger-icon>`}
|
||||
<h3 slot="header">
|
||||
${type !== "list" && (this.trigger as Exclude<Trigger, TriggerList>).id
|
||||
? html`<ha-trigger-id-chip
|
||||
id="trigger-id-chip"
|
||||
slot="leading-icon"
|
||||
.triggerId=${(this.trigger as Exclude<Trigger, TriggerList>).id}
|
||||
>
|
||||
</ha-trigger-id-chip>`
|
||||
: nothing}
|
||||
${describeTrigger(this.trigger, this.hass, this._entityReg)}
|
||||
${target !== undefined || (descriptionHasTarget && !this._isNew)
|
||||
? this._renderTargets(
|
||||
@@ -333,17 +321,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
)}
|
||||
</ha-dropdown-item>`
|
||||
: nothing}
|
||||
${type !== "list"
|
||||
? html`<ha-dropdown-item value="edit_id" .disabled=${this.disabled}>
|
||||
<ha-svg-icon slot="icon" .path=${mdiIdentifier}></ha-svg-icon>
|
||||
<div class="overflow-label">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.triggers.${"id" in this.trigger ? "edit" : "add"}_id`
|
||||
)}
|
||||
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
|
||||
</div>
|
||||
</ha-dropdown-item>`
|
||||
: nothing}
|
||||
<wa-divider></wa-divider>
|
||||
|
||||
<ha-dropdown-item value="duplicate" .disabled=${this.disabled}>
|
||||
@@ -717,7 +694,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
this.focus();
|
||||
}
|
||||
},
|
||||
editId: this._editTriggerId,
|
||||
rename: () => {
|
||||
this._renameTrigger();
|
||||
},
|
||||
@@ -829,34 +805,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _editTriggerId = () => {
|
||||
if (isTriggerList(this.trigger)) {
|
||||
return;
|
||||
}
|
||||
const trigger = this.trigger as Exclude<Trigger, TriggerList>;
|
||||
showEditTriggerIdDialog(this, {
|
||||
id: trigger.id,
|
||||
onUpdate: (newId) => {
|
||||
if (newId === (trigger.id ?? undefined)) {
|
||||
return;
|
||||
}
|
||||
const value: Trigger = { ...trigger };
|
||||
if (newId) {
|
||||
value.id = newId;
|
||||
} else {
|
||||
delete value.id;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value,
|
||||
});
|
||||
|
||||
if (this._selected && this.optionsInSidebar) {
|
||||
this.openSidebar(value); // refresh sidebar
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
private _renameTrigger = async (): Promise<void> => {
|
||||
if (isTriggerList(this.trigger)) return;
|
||||
const alias = await showPromptDialog(this, {
|
||||
@@ -1041,9 +989,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
case "edit_comment":
|
||||
this._editCommentTrigger();
|
||||
break;
|
||||
case "edit_id":
|
||||
this._editTriggerId();
|
||||
break;
|
||||
case "duplicate":
|
||||
this._duplicateTrigger();
|
||||
break;
|
||||
|
||||
@@ -8,7 +8,6 @@ import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import "../../../../components/ha-button";
|
||||
@@ -25,12 +24,12 @@ import type { TriggerDescriptions } from "../../../../data/trigger";
|
||||
import { isTriggerList, subscribeTriggers } from "../../../../data/trigger";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import { EDITOR_SAVE_FAB_TOAST_BOTTOM_OFFSET } from "../editor-toast";
|
||||
import { AutomationSortableListMixin } from "../ha-automation-sortable-list-mixin";
|
||||
import {
|
||||
getAddAutomationElementTargetFromQuery,
|
||||
PASTE_VALUE,
|
||||
showAddAutomationElementDialog,
|
||||
} from "../show-add-automation-element-dialog";
|
||||
import { AutomationSortableListMixin } from "../ha-automation-sortable-list-mixin";
|
||||
import { automationRowsStyles } from "../styles";
|
||||
import "./ha-automation-trigger-row";
|
||||
import type HaAutomationTriggerRow from "./ha-automation-trigger-row";
|
||||
@@ -68,38 +67,6 @@ export default class HaAutomationTrigger extends AutomationSortableListMixin<Tri
|
||||
this.highlightedTriggers = items;
|
||||
}
|
||||
|
||||
protected override pasteItem(ev: CustomEvent) {
|
||||
if (this.root && ev.detail.item) {
|
||||
const pasted = deepClone(ev.detail.item) as Trigger;
|
||||
ev.detail.item = pasted;
|
||||
}
|
||||
super.pasteItem(ev);
|
||||
}
|
||||
|
||||
protected override insertAfter(ev: CustomEvent) {
|
||||
// Only dedupe when a single trigger is being inserted.
|
||||
const incoming = ensureArray(ev.detail.value) as Trigger[];
|
||||
if (this.root && incoming.length === 1) {
|
||||
const trigger = deepClone(incoming[0]);
|
||||
ev.detail.value = trigger;
|
||||
}
|
||||
super.insertAfter(ev);
|
||||
}
|
||||
|
||||
protected override duplicateItem(ev: CustomEvent) {
|
||||
if (this.root) {
|
||||
const index = (ev.target as any).index;
|
||||
const duplicated = deepClone(this.triggers[index]);
|
||||
fireEvent(this, "value-changed", {
|
||||
// @ts-expect-error Requires library bump to ES2023
|
||||
value: this.triggers.toSpliced(index + 1, 0, duplicated),
|
||||
});
|
||||
ev.stopPropagation();
|
||||
return;
|
||||
}
|
||||
super.duplicateItem(ev);
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._unsubscribe();
|
||||
@@ -246,28 +213,23 @@ export default class HaAutomationTrigger extends AutomationSortableListMixin<Tri
|
||||
private _addTrigger = (value: string, target?: HassServiceTarget) => {
|
||||
let triggers: Trigger[];
|
||||
if (value === PASTE_VALUE) {
|
||||
const pasted = deepClone(this._clipboard!.trigger!);
|
||||
triggers = this.triggers.concat(pasted);
|
||||
triggers = this.triggers.concat(deepClone(this._clipboard!.trigger!));
|
||||
} else if (isDynamic(value)) {
|
||||
triggers = this.triggers.concat({
|
||||
trigger: getValueFromDynamic(value),
|
||||
target,
|
||||
});
|
||||
} else {
|
||||
let newTrigger: Trigger;
|
||||
if (isDynamic(value)) {
|
||||
newTrigger = {
|
||||
trigger: getValueFromDynamic(value),
|
||||
target,
|
||||
};
|
||||
} else {
|
||||
const trigger = value as Exclude<Trigger, TriggerList>["trigger"];
|
||||
const elClass = customElements.get(
|
||||
`ha-automation-trigger-${trigger}`
|
||||
) as CustomElementConstructor & {
|
||||
defaultConfig: Trigger;
|
||||
};
|
||||
newTrigger = {
|
||||
...elClass.defaultConfig,
|
||||
...(target?.entity_id ? { entity_id: target.entity_id } : {}),
|
||||
};
|
||||
}
|
||||
triggers = this.triggers.concat(newTrigger);
|
||||
const trigger = value as Exclude<Trigger, TriggerList>["trigger"];
|
||||
const elClass = customElements.get(
|
||||
`ha-automation-trigger-${trigger}`
|
||||
) as CustomElementConstructor & {
|
||||
defaultConfig: Trigger;
|
||||
};
|
||||
triggers = this.triggers.concat({
|
||||
...elClass.defaultConfig,
|
||||
...(target?.entity_id ? { entity_id: target.entity_id } : {}),
|
||||
});
|
||||
}
|
||||
this.focusLastItemOnChange = true;
|
||||
fireEvent(this, "value-changed", { value: triggers });
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { LitElement } from "lit";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
|
||||
export const loadEditTriggerIdDialog = () =>
|
||||
import("./ha-automation-edit-trigger-id-dialog");
|
||||
|
||||
export interface EditTriggerIdDialogParams {
|
||||
id?: string;
|
||||
onUpdate: (newId: string | undefined) => void;
|
||||
}
|
||||
|
||||
export const showEditTriggerIdDialog = (
|
||||
element: LitElement,
|
||||
dialogParams: EditTriggerIdDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
parentElement: element,
|
||||
dialogTag: "ha-automation-edit-trigger-id-dialog",
|
||||
dialogImport: loadEditTriggerIdDialog,
|
||||
dialogParams,
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
||||
import { consume } from "@lit/context";
|
||||
import { mdiCog, mdiContentCopy } from "@mdi/js";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@@ -13,10 +13,9 @@ import "../../../../../components/ha-dropdown-item";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/input/ha-input";
|
||||
import type { HaInput } from "../../../../../components/input/ha-input";
|
||||
import {
|
||||
automationConfigContext,
|
||||
type AutomationConfig,
|
||||
type WebhookTrigger,
|
||||
import type {
|
||||
AutomationConfig,
|
||||
WebhookTrigger,
|
||||
} from "../../../../../data/automation";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import { showEditorToast } from "../../editor-toast";
|
||||
@@ -34,9 +33,9 @@ export class HaWebhookTrigger extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@consume({ context: automationConfigContext, subscribe: true })
|
||||
@state()
|
||||
private _config?: AutomationConfig;
|
||||
@state() private _config?: AutomationConfig;
|
||||
|
||||
private _unsub?: UnsubscribeFunc;
|
||||
|
||||
public static get defaultConfig(): WebhookTrigger {
|
||||
return {
|
||||
@@ -47,6 +46,24 @@ export class HaWebhookTrigger extends LitElement {
|
||||
};
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
const details = {
|
||||
callback: (config) => {
|
||||
this._config = config;
|
||||
},
|
||||
};
|
||||
fireEvent(this, "subscribe-automation-config", details);
|
||||
this._unsub = (details as any).unsub;
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this._unsub) {
|
||||
this._unsub();
|
||||
}
|
||||
}
|
||||
|
||||
private _generateWebhookId(): string {
|
||||
// The webhook_id should be treated like a password. Generate a default
|
||||
// value that would be hard for someone to guess. This generates a
|
||||
|
||||
@@ -2,16 +2,10 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import {
|
||||
mdiPalette,
|
||||
mdiPlayCircleOutline,
|
||||
mdiPlaylistCheck,
|
||||
mdiRobotOutline,
|
||||
mdiScriptTextOutline,
|
||||
} from "@mdi/js";
|
||||
import { computeDeviceNameDisplay } from "../../../../common/entity/compute_device_name";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-adaptive-dialog";
|
||||
import "../../../../components/ha-icon";
|
||||
import "../../../../components/ha-list";
|
||||
import "../../../../components/ha-list-item";
|
||||
import "../../../../components/ha-spinner";
|
||||
@@ -38,10 +32,14 @@ import { showScriptEditor } from "../../../../data/script";
|
||||
import type { SceneEntities } from "../../../../data/scene";
|
||||
import { showSceneEditor } from "../../../../data/scene";
|
||||
import {
|
||||
ADD_TO_ACTION_ICONS,
|
||||
addToActionHandler,
|
||||
type AddToActionKey,
|
||||
getAddToActionLabel,
|
||||
getAddToActionLabelParts,
|
||||
} from "../../../../dialogs/more-info/add-to";
|
||||
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
||||
import "../../automation/target/ha-automation-target-badge";
|
||||
import type { DeviceAddToDialogParams } from "./show-dialog-device-add-to";
|
||||
|
||||
@customElement("dialog-device-add-to")
|
||||
@@ -151,11 +149,7 @@ export class DialogDeviceAddTo extends LitElement {
|
||||
if (!this._params) {
|
||||
return nothing;
|
||||
}
|
||||
const deviceName = computeDeviceNameDisplay(
|
||||
this._params.device,
|
||||
this._i18n.localize,
|
||||
this._states
|
||||
);
|
||||
const deviceName = this._deviceName;
|
||||
|
||||
return html`
|
||||
<h3 class="section-header">
|
||||
@@ -169,39 +163,51 @@ export class DialogDeviceAddTo extends LitElement {
|
||||
data-type="automation_trigger"
|
||||
@click=${this._handleNewAction}
|
||||
data-dialog="close"
|
||||
>
|
||||
<ha-svg-icon slot="graphic" .path=${mdiRobotOutline}></ha-svg-icon>
|
||||
${this._i18n.localize(
|
||||
"ui.dialogs.more_info_control.add_to.actions.automation_trigger",
|
||||
{ target: deviceName }
|
||||
aria-label=${getAddToActionLabel(
|
||||
this._i18n.localize,
|
||||
"automation_trigger",
|
||||
deviceName
|
||||
)}
|
||||
>
|
||||
<ha-icon
|
||||
slot="graphic"
|
||||
.icon=${ADD_TO_ACTION_ICONS.automation_trigger}
|
||||
></ha-icon>
|
||||
${this._renderActionLabel("automation_trigger")}
|
||||
</ha-list-item>
|
||||
<ha-list-item
|
||||
graphic="icon"
|
||||
data-type="automation_condition"
|
||||
@click=${this._handleNewAction}
|
||||
data-dialog="close"
|
||||
>
|
||||
<ha-svg-icon slot="graphic" .path=${mdiPlaylistCheck}></ha-svg-icon>
|
||||
${this._i18n.localize(
|
||||
"ui.dialogs.more_info_control.add_to.actions.automation_condition",
|
||||
{ target: deviceName }
|
||||
aria-label=${getAddToActionLabel(
|
||||
this._i18n.localize,
|
||||
"automation_condition",
|
||||
deviceName
|
||||
)}
|
||||
>
|
||||
<ha-icon
|
||||
slot="graphic"
|
||||
.icon=${ADD_TO_ACTION_ICONS.automation_condition}
|
||||
></ha-icon>
|
||||
${this._renderActionLabel("automation_condition")}
|
||||
</ha-list-item>
|
||||
<ha-list-item
|
||||
graphic="icon"
|
||||
data-type="automation_action"
|
||||
@click=${this._handleNewAction}
|
||||
data-dialog="close"
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiPlayCircleOutline}
|
||||
></ha-svg-icon>
|
||||
${this._i18n.localize(
|
||||
"ui.dialogs.more_info_control.add_to.actions.automation_action",
|
||||
{ target: deviceName }
|
||||
aria-label=${getAddToActionLabel(
|
||||
this._i18n.localize,
|
||||
"automation_action",
|
||||
deviceName
|
||||
)}
|
||||
>
|
||||
<ha-icon
|
||||
slot="graphic"
|
||||
.icon=${ADD_TO_ACTION_ICONS.automation_action}
|
||||
></ha-icon>
|
||||
${this._renderActionLabel("automation_action")}
|
||||
</ha-list-item>
|
||||
</ha-list>
|
||||
<h3 class="section-header">
|
||||
@@ -213,15 +219,17 @@ export class DialogDeviceAddTo extends LitElement {
|
||||
data-type="script_action"
|
||||
@click=${this._handleNewAction}
|
||||
data-dialog="close"
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiScriptTextOutline}
|
||||
></ha-svg-icon>
|
||||
${this._i18n.localize(
|
||||
"ui.dialogs.more_info_control.add_to.actions.script_action",
|
||||
{ target: deviceName }
|
||||
aria-label=${getAddToActionLabel(
|
||||
this._i18n.localize,
|
||||
"script_action",
|
||||
deviceName
|
||||
)}
|
||||
>
|
||||
<ha-icon
|
||||
slot="graphic"
|
||||
.icon=${ADD_TO_ACTION_ICONS.script_action}
|
||||
></ha-icon>
|
||||
${this._renderActionLabel("script_action")}
|
||||
</ha-list-item>
|
||||
</ha-list>
|
||||
${this._renderSceneSection(deviceName)}
|
||||
@@ -242,11 +250,7 @@ export class DialogDeviceAddTo extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const deviceName = computeDeviceNameDisplay(
|
||||
this._params.device,
|
||||
this._i18n.localize,
|
||||
this._states
|
||||
);
|
||||
const deviceName = this._deviceName;
|
||||
|
||||
const hasTriggers = Boolean(this._triggers?.length);
|
||||
const hasConditions = Boolean(this._conditions?.length);
|
||||
@@ -279,15 +283,17 @@ export class DialogDeviceAddTo extends LitElement {
|
||||
data-type="trigger"
|
||||
@click=${this._handleLegacyAction}
|
||||
data-dialog="close"
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiRobotOutline}
|
||||
></ha-svg-icon>
|
||||
${this._i18n.localize(
|
||||
"ui.dialogs.more_info_control.add_to.actions.automation_trigger",
|
||||
{ target: deviceName }
|
||||
aria-label=${getAddToActionLabel(
|
||||
this._i18n.localize,
|
||||
"automation_trigger",
|
||||
deviceName
|
||||
)}
|
||||
>
|
||||
<ha-icon
|
||||
slot="graphic"
|
||||
.icon=${ADD_TO_ACTION_ICONS.automation_trigger}
|
||||
></ha-icon>
|
||||
${this._renderActionLabel("automation_trigger")}
|
||||
</ha-list-item>
|
||||
`
|
||||
: nothing}
|
||||
@@ -298,15 +304,17 @@ export class DialogDeviceAddTo extends LitElement {
|
||||
data-type="condition"
|
||||
@click=${this._handleLegacyAction}
|
||||
data-dialog="close"
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiPlaylistCheck}
|
||||
></ha-svg-icon>
|
||||
${this._i18n.localize(
|
||||
"ui.dialogs.more_info_control.add_to.actions.automation_condition",
|
||||
{ target: deviceName }
|
||||
aria-label=${getAddToActionLabel(
|
||||
this._i18n.localize,
|
||||
"automation_condition",
|
||||
deviceName
|
||||
)}
|
||||
>
|
||||
<ha-icon
|
||||
slot="graphic"
|
||||
.icon=${ADD_TO_ACTION_ICONS.automation_condition}
|
||||
></ha-icon>
|
||||
${this._renderActionLabel("automation_condition")}
|
||||
</ha-list-item>
|
||||
`
|
||||
: nothing}
|
||||
@@ -317,15 +325,17 @@ export class DialogDeviceAddTo extends LitElement {
|
||||
data-type="automation_action"
|
||||
@click=${this._handleLegacyAction}
|
||||
data-dialog="close"
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiPlayCircleOutline}
|
||||
></ha-svg-icon>
|
||||
${this._i18n.localize(
|
||||
"ui.dialogs.more_info_control.add_to.actions.automation_action",
|
||||
{ target: deviceName }
|
||||
aria-label=${getAddToActionLabel(
|
||||
this._i18n.localize,
|
||||
"automation_action",
|
||||
deviceName
|
||||
)}
|
||||
>
|
||||
<ha-icon
|
||||
slot="graphic"
|
||||
.icon=${ADD_TO_ACTION_ICONS.automation_action}
|
||||
></ha-icon>
|
||||
${this._renderActionLabel("automation_action")}
|
||||
</ha-list-item>
|
||||
`
|
||||
: nothing}
|
||||
@@ -351,15 +361,17 @@ export class DialogDeviceAddTo extends LitElement {
|
||||
data-type="script_action"
|
||||
@click=${this._handleLegacyAction}
|
||||
data-dialog="close"
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiScriptTextOutline}
|
||||
></ha-svg-icon>
|
||||
${this._i18n.localize(
|
||||
"ui.dialogs.more_info_control.add_to.actions.script_action",
|
||||
{ target: deviceName }
|
||||
aria-label=${getAddToActionLabel(
|
||||
this._i18n.localize,
|
||||
"script_action",
|
||||
deviceName
|
||||
)}
|
||||
>
|
||||
<ha-icon
|
||||
slot="graphic"
|
||||
.icon=${ADD_TO_ACTION_ICONS.script_action}
|
||||
></ha-icon>
|
||||
${this._renderActionLabel("script_action")}
|
||||
</ha-list-item>
|
||||
</ha-list>
|
||||
`
|
||||
@@ -390,17 +402,48 @@ export class DialogDeviceAddTo extends LitElement {
|
||||
graphic="icon"
|
||||
@click=${this._handleCreateScene}
|
||||
data-dialog="close"
|
||||
>
|
||||
<ha-svg-icon slot="graphic" .path=${mdiPalette}></ha-svg-icon>
|
||||
${this._i18n.localize(
|
||||
"ui.dialogs.more_info_control.add_to.actions.scene",
|
||||
{ target: deviceName }
|
||||
aria-label=${getAddToActionLabel(
|
||||
this._i18n.localize,
|
||||
"scene",
|
||||
deviceName
|
||||
)}
|
||||
>
|
||||
<ha-icon slot="graphic" .icon=${ADD_TO_ACTION_ICONS.scene}></ha-icon>
|
||||
${this._renderActionLabel("scene")}
|
||||
</ha-list-item>
|
||||
</ha-list>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderActionLabel(key: AddToActionKey) {
|
||||
const [beforeTarget, afterTarget] = getAddToActionLabelParts(
|
||||
this._i18n.localize,
|
||||
key
|
||||
);
|
||||
|
||||
return html`<span class="action-label">
|
||||
${beforeTarget}${this._renderDeviceTarget()}${afterTarget}
|
||||
</span>`;
|
||||
}
|
||||
|
||||
private _renderDeviceTarget() {
|
||||
return html`<ha-automation-target-badge
|
||||
target-type="device"
|
||||
.targetId=${this._params?.device.id}
|
||||
.label=${this._deviceName}
|
||||
></ha-automation-target-badge>`;
|
||||
}
|
||||
|
||||
private get _deviceName(): string {
|
||||
return this._params
|
||||
? computeDeviceNameDisplay(
|
||||
this._params.device,
|
||||
this._i18n.localize,
|
||||
this._states
|
||||
)
|
||||
: "";
|
||||
}
|
||||
|
||||
private _handleNewAction(ev: Event) {
|
||||
if (!this._params) {
|
||||
return;
|
||||
@@ -477,6 +520,13 @@ export class DialogDeviceAddTo extends LitElement {
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.action-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-1);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name";
|
||||
import { computeFloorName } from "../../../common/entity/compute_floor_name";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import { getDeviceArea } from "../../../common/entity/context/get_device_context";
|
||||
import {
|
||||
PROTOCOL_INTEGRATIONS,
|
||||
protocolIntegrationPicked,
|
||||
@@ -59,7 +58,6 @@ import {
|
||||
deserializeFilters,
|
||||
serializeFilters,
|
||||
} from "../../../data/data_table_filters";
|
||||
import { computeDeviceAreaLabel } from "../../../data/device/device_picker";
|
||||
import type {
|
||||
DeviceEntityLookup,
|
||||
DeviceRegistryEntry,
|
||||
@@ -460,29 +458,17 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
.map((lbl) => labelReg!.find((label) => label.label_id === lbl))
|
||||
.filter((entry): entry is LabelRegistryEntry => entry !== undefined);
|
||||
|
||||
const { areaName } = computeDeviceAreaLabel(
|
||||
device,
|
||||
this.hass.areas,
|
||||
this.hass.devices,
|
||||
this.hass.states,
|
||||
this.hass.localize,
|
||||
this.hass.language,
|
||||
this.hass.translationMetadata,
|
||||
device.via_device_id
|
||||
? deviceEntityLookup[device.via_device_id]
|
||||
: undefined
|
||||
);
|
||||
|
||||
const floorArea =
|
||||
getDeviceArea(device, areas) ??
|
||||
(device.via_device_id && this.hass.devices[device.via_device_id]
|
||||
? getDeviceArea(this.hass.devices[device.via_device_id], areas)
|
||||
: undefined);
|
||||
const floorId = floorArea?.floor_id;
|
||||
const floorName =
|
||||
floorId && this.hass.floors?.[floorId]
|
||||
? computeFloorName(this.hass.floors[floorId])
|
||||
: undefined;
|
||||
let floorName;
|
||||
if (
|
||||
device.area_id &&
|
||||
areas[device.area_id]?.floor_id &&
|
||||
this.hass.floors
|
||||
) {
|
||||
const floorId = areas[device.area_id].floor_id;
|
||||
if (this.hass.floors[floorId!]) {
|
||||
floorName = computeFloorName(this.hass.floors[floorId!]);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...device,
|
||||
@@ -498,7 +484,10 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
manufacturer:
|
||||
device.manufacturer ||
|
||||
`<${localize("ui.panel.config.devices.data_table.unknown")}>`,
|
||||
area: areaName,
|
||||
area:
|
||||
device.area_id && areas[device.area_id]
|
||||
? areas[device.area_id].name
|
||||
: undefined,
|
||||
floor: floorName,
|
||||
integration: deviceEntries.length
|
||||
? deviceEntries
|
||||
|
||||
@@ -5164,9 +5164,7 @@
|
||||
},
|
||||
"id": "Trigger ID",
|
||||
"optional": "Optional",
|
||||
"add_id": "Add trigger ID",
|
||||
"edit_id": "Edit trigger ID",
|
||||
"id_description": "Use trigger IDs in a Triggered by condition to run different actions depending on which trigger started the automation.",
|
||||
"edit_id": "Edit ID",
|
||||
"duplicate": "[%key:ui::common::duplicate%]",
|
||||
"re_order": "Re-order",
|
||||
"rename": "Rename",
|
||||
@@ -5596,8 +5594,7 @@
|
||||
"id": "Trigger",
|
||||
"description": {
|
||||
"picker": "Tests if the automation has been triggered by a specific trigger.",
|
||||
"full": "If triggered by {id}",
|
||||
"no_trigger": "No trigger selected"
|
||||
"full": "If triggered by {id}"
|
||||
}
|
||||
},
|
||||
"zone": {
|
||||
|
||||
@@ -6666,10 +6666,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"date-fns@npm:4.2.1":
|
||||
version: 4.2.1
|
||||
resolution: "date-fns@npm:4.2.1"
|
||||
checksum: 10/c2731946a2d022935b3641062cd64bcdd62a65066115fee3490ba360a17fbf42134918493c43acb2c5c995b9ea4a2c7f505f0d9c02e73351cd2899b13e081729
|
||||
"date-fns@npm:4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "date-fns@npm:4.2.0"
|
||||
checksum: 10/6f50c3da98286a93807ad7c1b790ba753d6ea17d028cf0ffe39aa208693d940100a9069ec444f9436dc010de6a94242b8cb459e86e92e8d1948c553f4d141f71
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -8635,7 +8635,7 @@ __metadata:
|
||||
core-js: "npm:3.49.0"
|
||||
cropperjs: "npm:1.6.2"
|
||||
culori: "npm:4.0.2"
|
||||
date-fns: "npm:4.2.1"
|
||||
date-fns: "npm:4.2.0"
|
||||
deep-clone-simple: "npm:1.1.1"
|
||||
deep-freeze: "npm:0.0.1"
|
||||
del: "npm:8.0.1"
|
||||
|
||||
Reference in New Issue
Block a user