Add floor and label support to describe action (#20403)

This commit is contained in:
Bram Kragten 2024-04-04 13:00:05 +02:00 committed by GitHub
parent 2d4a8e2e45
commit 08b25f9c2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 145 additions and 28 deletions

View File

@ -136,7 +136,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
@ -149,7 +149,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>
` `

View File

@ -1,3 +1,4 @@
import { consume } from "@lit-labs/context";
import { import {
mdiAlertCircle, mdiAlertCircle,
mdiCircle, mdiCircle,
@ -6,14 +7,13 @@ import {
mdiProgressWrench, mdiProgressWrench,
mdiRecordCircleOutline, mdiRecordCircleOutline,
} from "@mdi/js"; } from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { import {
css,
CSSResultGroup, CSSResultGroup,
html,
LitElement, LitElement,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
css,
html,
nothing, nothing,
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
@ -23,27 +23,31 @@ 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 {
EntityRegistryEntry, floorsContext,
subscribeEntityRegistry, fullEntitiesContext,
} from "../../data/entity_registry"; labelsContext,
} from "../../data/context";
import { EntityRegistryEntry } from "../../data/entity_registry";
import { FloorRegistryEntry } from "../../data/floor_registry";
import { LabelRegistryEntry } from "../../data/label_registry";
import { LogbookEntry } from "../../data/logbook"; import { LogbookEntry } from "../../data/logbook";
import { import {
ChooseAction, ChooseAction,
ChooseActionChoice, ChooseActionChoice,
getActionType,
IfAction, IfAction,
ParallelAction, ParallelAction,
RepeatAction, RepeatAction,
getActionType,
} from "../../data/script"; } from "../../data/script";
import { describeAction } from "../../data/script_i18n"; import { describeAction } from "../../data/script_i18n";
import { import {
ActionTraceStep, ActionTraceStep,
AutomationTraceExtended, AutomationTraceExtended,
ChooseActionTraceStep, ChooseActionTraceStep,
getDataFromPath,
IfActionTraceStep, IfActionTraceStep,
isTriggerPath,
TriggerTraceStep, TriggerTraceStep,
getDataFromPath,
isTriggerPath,
} from "../../data/trace"; } from "../../data/trace";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import "./ha-timeline"; import "./ha-timeline";
@ -200,6 +204,8 @@ class ActionRenderer {
constructor( constructor(
private hass: HomeAssistant, private hass: HomeAssistant,
private entityReg: EntityRegistryEntry[], private entityReg: EntityRegistryEntry[],
private labelReg: LabelRegistryEntry[],
private floorReg: FloorRegistryEntry[],
private entries: TemplateResult[], private entries: TemplateResult[],
private trace: AutomationTraceExtended, private trace: AutomationTraceExtended,
private logbookRenderer: LogbookRenderer, private logbookRenderer: LogbookRenderer,
@ -310,7 +316,14 @@ class ActionRenderer {
this._renderEntry( this._renderEntry(
path, path,
describeAction(this.hass, this.entityReg, data, actionType), describeAction(
this.hass,
this.entityReg,
this.labelReg,
this.floorReg,
data,
actionType
),
undefined, undefined,
data.enabled === false data.enabled === false
); );
@ -475,7 +488,13 @@ class ActionRenderer {
const name = const name =
repeatConfig.alias || repeatConfig.alias ||
describeAction(this.hass, this.entityReg, repeatConfig); describeAction(
this.hass,
this.entityReg,
this.labelReg,
this.floorReg,
repeatConfig
);
this._renderEntry(repeatPath, name, undefined, disabled); this._renderEntry(repeatPath, name, undefined, disabled);
@ -631,15 +650,17 @@ export class HaAutomationTracer extends LitElement {
@property({ type: Boolean }) public allowPick = false; @property({ type: Boolean }) public allowPick = false;
@state() private _entityReg: EntityRegistryEntry[] = []; @state()
@consume({ context: fullEntitiesContext, subscribe: true })
_entityReg!: EntityRegistryEntry[];
public hassSubscribe(): UnsubscribeFunc[] { @state()
return [ @consume({ context: labelsContext, subscribe: true })
subscribeEntityRegistry(this.hass.connection!, (entities) => { _labelReg!: LabelRegistryEntry[];
this._entityReg = entities;
}), @state()
]; @consume({ context: floorsContext, subscribe: true })
} _floorReg!: FloorRegistryEntry[];
protected render() { protected render() {
if (!this.trace) { if (!this.trace) {
@ -657,6 +678,8 @@ 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,

View File

@ -2,6 +2,8 @@ import { createContext } from "@lit-labs/context";
import { HassConfig } from "home-assistant-js-websocket"; import { HassConfig } from "home-assistant-js-websocket";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
import { EntityRegistryEntry } from "./entity_registry"; import { EntityRegistryEntry } from "./entity_registry";
import { FloorRegistryEntry } from "./floor_registry";
import { LabelRegistryEntry } from "./label_registry";
export const connectionContext = export const connectionContext =
createContext<HomeAssistant["connection"]>("connection"); createContext<HomeAssistant["connection"]>("connection");
@ -25,3 +27,7 @@ export const panelsContext = createContext<HomeAssistant["panels"]>("panels");
export const fullEntitiesContext = export const fullEntitiesContext =
createContext<EntityRegistryEntry[]>("extendedEntities"); createContext<EntityRegistryEntry[]>("extendedEntities");
export const floorsContext = createContext<FloorRegistryEntry[]>("floors");
export const labelsContext = createContext<LabelRegistryEntry[]>("labels");

View File

@ -14,7 +14,9 @@ import {
computeEntityRegistryName, computeEntityRegistryName,
entityRegistryById, entityRegistryById,
} from "./entity_registry"; } from "./entity_registry";
import { FloorRegistryEntry } from "./floor_registry";
import { domainToName } from "./integration"; import { domainToName } from "./integration";
import { LabelRegistryEntry } from "./label_registry";
import { import {
ActionType, ActionType,
ActionTypes, ActionTypes,
@ -40,6 +42,8 @@ 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: FloorRegistryEntry[],
action: ActionTypes[T], action: ActionTypes[T],
actionType?: T, actionType?: T,
ignoreAlias = false ignoreAlias = false
@ -48,6 +52,8 @@ export const describeAction = <T extends ActionType>(
return tryDescribeAction( return tryDescribeAction(
hass, hass,
entityRegistry, entityRegistry,
labelRegistry,
floorRegistry,
action, action,
actionType, actionType,
ignoreAlias ignoreAlias
@ -66,6 +72,8 @@ 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: FloorRegistryEntry[],
action: ActionTypes[T], action: ActionTypes[T],
actionType?: T, actionType?: T,
ignoreAlias = false ignoreAlias = false
@ -82,10 +90,12 @@ const tryDescribeAction = <T extends ActionType>(
const targets: string[] = []; const targets: string[] = [];
if (config.target) { if (config.target) {
for (const [key, label] of Object.entries({ for (const [key, name] of Object.entries({
area_id: "areas", area_id: "areas",
device_id: "devices", device_id: "devices",
entity_id: "entities", entity_id: "entities",
floor_id: "floors",
label_id: "labels",
})) { })) {
if (!(key in config.target)) { if (!(key in config.target)) {
continue; continue;
@ -99,7 +109,7 @@ const tryDescribeAction = <T extends ActionType>(
targets.push( targets.push(
hass.localize( hass.localize(
`${actionTranslationBaseKey}.service.description.target_template`, `${actionTranslationBaseKey}.service.description.target_template`,
{ name: label } { name }
) )
); );
break; break;
@ -147,6 +157,32 @@ const tryDescribeAction = <T extends ActionType>(
) )
); );
} }
} else if (key === "floor_id") {
const floor = floorRegistry.find(
(flr) => flr.floor_id === targetThing
);
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 { } else {
targets.push(targetThing); targets.push(targetThing);
} }

View File

@ -42,8 +42,14 @@ import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import { ACTION_ICONS, YAML_ONLY_ACTION_TYPES } from "../../../../data/action"; import { ACTION_ICONS, YAML_ONLY_ACTION_TYPES } from "../../../../data/action";
import { AutomationClipboard } from "../../../../data/automation"; import { AutomationClipboard } from "../../../../data/automation";
import { validateConfig } from "../../../../data/config"; import { validateConfig } from "../../../../data/config";
import { fullEntitiesContext } from "../../../../data/context"; import {
floorsContext,
fullEntitiesContext,
labelsContext,
} from "../../../../data/context";
import { EntityRegistryEntry } from "../../../../data/entity_registry"; import { EntityRegistryEntry } from "../../../../data/entity_registry";
import { FloorRegistryEntry } from "../../../../data/floor_registry";
import { LabelRegistryEntry } from "../../../../data/label_registry";
import { import {
Action, Action,
NonConditionAction, NonConditionAction,
@ -146,6 +152,14 @@ 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!: FloorRegistryEntry[];
@state() private _warnings?: string[]; @state() private _warnings?: string[];
@state() private _uiModeAvailable = true; @state() private _uiModeAvailable = true;
@ -210,7 +224,13 @@ export default class HaAutomationActionRow extends LitElement {
.path=${ACTION_ICONS[type!]} .path=${ACTION_ICONS[type!]}
></ha-svg-icon>`} ></ha-svg-icon>`}
${capitalizeFirstLetter( ${capitalizeFirstLetter(
describeAction(this.hass, this._entityReg, this.action) describeAction(
this.hass,
this._entityReg,
this._labelReg,
this._floorReg,
this.action
)
)} )}
</h3> </h3>
@ -573,7 +593,15 @@ export default class HaAutomationActionRow extends LitElement {
), ),
inputType: "string", inputType: "string",
placeholder: capitalizeFirstLetter( placeholder: capitalizeFirstLetter(
describeAction(this.hass, this._entityReg, this.action, undefined, true) describeAction(
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"),

View File

@ -35,7 +35,11 @@ import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { listenMediaQuery } from "../../common/dom/media_query"; import { listenMediaQuery } from "../../common/dom/media_query";
import { CloudStatus, fetchCloudStatus } from "../../data/cloud"; import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
import { fullEntitiesContext } from "../../data/context"; import {
floorsContext,
fullEntitiesContext,
labelsContext,
} from "../../data/context";
import { import {
entityRegistryByEntityId, entityRegistryByEntityId,
entityRegistryById, entityRegistryById,
@ -45,6 +49,8 @@ import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
import { PageNavigation } from "../../layouts/hass-tabs-subpage"; import { PageNavigation } from "../../layouts/hass-tabs-subpage";
import { SubscribeMixin } from "../../mixins/subscribe-mixin"; import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { HomeAssistant, Route } from "../../types"; import { HomeAssistant, Route } from "../../types";
import { subscribeLabelRegistry } from "../../data/label_registry";
import { subscribeFloorRegistry } from "../../data/floor_registry";
declare global { declare global {
// for fire event // for fire event
@ -379,11 +385,27 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
initialValue: [], initialValue: [],
}); });
private _labelsContext = new ContextProvider(this, {
context: labelsContext,
initialValue: [],
});
private _floorsContext = new ContextProvider(this, {
context: floorsContext,
initialValue: [],
});
public hassSubscribe(): UnsubscribeFunc[] { public hassSubscribe(): UnsubscribeFunc[] {
return [ return [
subscribeEntityRegistry(this.hass.connection!, (entities) => { subscribeEntityRegistry(this.hass.connection!, (entities) => {
this._entitiesContext.setValue(entities); this._entitiesContext.setValue(entities);
}), }),
subscribeLabelRegistry(this.hass.connection!, (labels) => {
this._labelsContext.setValue(labels);
}),
subscribeFloorRegistry(this.hass.connection!, (floors) => {
this._floorsContext.setValue(floors);
}),
]; ];
} }

View File

@ -3241,7 +3241,9 @@
"target_template": "templated {name}", "target_template": "templated {name}",
"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_label": "unknown label"
} }
}, },
"play_media": { "play_media": {