Change yaml config of UI service call actions (#21508)

This commit is contained in:
Bram Kragten 2024-07-31 14:34:43 +02:00 committed by GitHub
parent 0a095c6f21
commit a88a7c5236
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 71 additions and 32 deletions

View File

@ -287,11 +287,11 @@ const CONFIGS = [
config: ` config: `
- type: entities - type: entities
entities: entities:
- type: call-service - type: perform-action
icon: mdi:power icon: mdi:power
name: Bed light name: Bed light
action_name: Toggle light action_name: Toggle light
service: light.toggle action: light.toggle
data: data:
entity_id: light.bed_light entity_id: light.bed_light
- type: section - type: section

View File

@ -5,10 +5,12 @@ export interface ToggleActionConfig extends BaseActionConfig {
} }
export interface CallServiceActionConfig extends BaseActionConfig { export interface CallServiceActionConfig extends BaseActionConfig {
action: "call-service"; action: "call-service" | "perform-action";
service: string; /** @deprecated "service" is kept for backwards compatibility. Replaced by "perform_action". */
service?: string;
perform_action: string;
target?: HassServiceTarget; target?: HassServiceTarget;
// "service_data" is kept for backwards compatibility. Replaced by "data". /** @deprecated "service_data" is kept for backwards compatibility. Replaced by "data". */
service_data?: Record<string, unknown>; service_data?: Record<string, unknown>;
data?: Record<string, unknown>; data?: Record<string, unknown>;
} }

View File

@ -302,7 +302,9 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
state_color: this._config.state_color, state_color: this._config.state_color,
...(entityConf as EntityConfig), ...(entityConf as EntityConfig),
} as EntityConfig) } as EntityConfig)
: entityConf : entityConf.type === "perform-action"
? { ...entityConf, type: "call-service" }
: entityConf
); );
if (this._hass) { if (this._hass) {
element.hass = this._hass; element.hass = this._hass;

View File

@ -66,6 +66,8 @@ export interface EntitiesCardEntityConfig extends EntityConfig {
| "tilt-position" | "tilt-position"
| "brightness"; | "brightness";
action_name?: string; action_name?: string;
action?: string;
/** @deprecated use "action" instead */
service?: string; service?: string;
// "service_data" is kept for backwards compatibility. Replaced by "data". // "service_data" is kept for backwards compatibility. Replaced by "data".
service_data?: Record<string, unknown>; service_data?: Record<string, unknown>;

View File

@ -56,8 +56,12 @@ export const handleAction = async (
forwardHaptic("warning"); forwardHaptic("warning");
let serviceName; let serviceName;
if (actionConfig.action === "call-service") { if (
const [domain, service] = actionConfig.service.split(".", 2); actionConfig.action === "call-service" ||
actionConfig.action === "perform-action"
) {
const [domain, service] = (actionConfig.perform_action ||
actionConfig.service)!.split(".", 2);
const serviceDomains = hass.services; const serviceDomains = hass.services;
if (domain in serviceDomains && service in serviceDomains[domain]) { if (domain in serviceDomains && service in serviceDomains[domain]) {
await hass.loadBackendTranslation("title"); await hass.loadBackendTranslation("title");
@ -145,15 +149,17 @@ export const handleAction = async (
} }
break; break;
} }
case "perform-action":
case "call-service": { case "call-service": {
if (!actionConfig.service) { if (!actionConfig.perform_action && !actionConfig.service) {
showToast(node, { showToast(node, {
message: hass.localize("ui.panel.lovelace.cards.actions.no_action"), message: hass.localize("ui.panel.lovelace.cards.actions.no_action"),
}); });
forwardHaptic("failure"); forwardHaptic("failure");
return; return;
} }
const [domain, service] = actionConfig.service.split(".", 2); const [domain, service] = (actionConfig.perform_action ||
actionConfig.service)!.split(".", 2);
hass.callService( hass.callService(
domain, domain,
service, service,

View File

@ -33,7 +33,7 @@ const DEFAULT_ACTIONS: UiAction[] = [
"toggle", "toggle",
"navigate", "navigate",
"url", "url",
"call-service", "perform-action",
"assist", "assist",
"none", "none",
]; ];
@ -98,7 +98,7 @@ export class HuiActionEditor extends LitElement {
get _service(): string { get _service(): string {
const config = this.config as CallServiceActionConfig; const config = this.config as CallServiceActionConfig;
return config?.service || ""; return config?.perform_action || config?.service || "";
} }
private _serviceAction = memoizeOne( private _serviceAction = memoizeOne(
@ -127,13 +127,19 @@ export class HuiActionEditor extends LitElement {
const actions = this.actions ?? DEFAULT_ACTIONS; const actions = this.actions ?? DEFAULT_ACTIONS;
let action = this.config?.action || "default";
if (action === "call-service") {
action = "perform-action";
}
return html` return html`
<div class="dropdown"> <div class="dropdown">
<ha-select <ha-select
.label=${this.label} .label=${this.label}
.configValue=${"action"} .configValue=${"action"}
@selected=${this._actionPicked} @selected=${this._actionPicked}
.value=${this.config?.action ?? "default"} .value=${action}
@closed=${stopPropagation} @closed=${stopPropagation}
fixedMenuPosition fixedMenuPosition
naturalMenuWidt naturalMenuWidt
@ -149,10 +155,10 @@ export class HuiActionEditor extends LitElement {
: nothing} : nothing}
</mwc-list-item> </mwc-list-item>
${actions.map( ${actions.map(
(action) => html` (actn) => html`
<mwc-list-item .value=${action}> <mwc-list-item .value=${actn}>
${this.hass!.localize( ${this.hass!.localize(
`ui.panel.lovelace.editor.action-editor.actions.${action}` `ui.panel.lovelace.editor.action-editor.actions.${actn}`
)} )}
</mwc-list-item> </mwc-list-item>
` `
@ -188,7 +194,8 @@ export class HuiActionEditor extends LitElement {
></ha-textfield> ></ha-textfield>
` `
: nothing} : nothing}
${this.config?.action === "call-service" ${this.config?.action === "call-service" ||
this.config?.action === "perform-action"
? html` ? html`
<ha-service-control <ha-service-control
.hass=${this.hass} .hass=${this.hass}
@ -234,8 +241,8 @@ export class HuiActionEditor extends LitElement {
data = { url_path: this._url_path }; data = { url_path: this._url_path };
break; break;
} }
case "call-service": { case "perform-action": {
data = { service: this._service }; data = { perform_action: this._service };
break; break;
} }
case "navigate": { case "navigate": {
@ -285,7 +292,7 @@ export class HuiActionEditor extends LitElement {
ev.stopPropagation(); ev.stopPropagation();
const value = { const value = {
...this.config!, ...this.config!,
service: ev.detail.value.service || "", perform_action: ev.detail.value.service || "",
data: ev.detail.value.data, data: ev.detail.value.data,
target: ev.detail.value.target || {}, target: ev.detail.value.target || {},
}; };
@ -296,6 +303,9 @@ export class HuiActionEditor extends LitElement {
if ("service_data" in value) { if ("service_data" in value) {
delete value.service_data; delete value.service_data;
} }
if ("service" in value) {
delete value.service;
}
fireEvent(this, "value-changed", { value }); fireEvent(this, "value-changed", { value });
} }

View File

@ -67,9 +67,10 @@ const castEntitiesRowConfigStruct = object({
}); });
const callServiceEntitiesRowConfigStruct = object({ const callServiceEntitiesRowConfigStruct = object({
type: literal("call-service"), type: enums(["call-service", "perform-action"]),
name: string(), name: string(),
service: string(), service: optional(string()),
action: optional(string()),
icon: optional(string()), icon: optional(string()),
action_name: optional(string()), action_name: optional(string()),
// "service_data" is kept for backwards compatibility. Replaced by "data". // "service_data" is kept for backwards compatibility. Replaced by "data".
@ -149,6 +150,7 @@ const entitiesRowConfigStruct = dynamic<any>((value) => {
case "buttons": { case "buttons": {
return buttonsEntitiesRowConfigStruct; return buttonsEntitiesRowConfigStruct;
} }
case "perform-action":
case "call-service": { case "call-service": {
return callServiceEntitiesRowConfigStruct; return callServiceEntitiesRowConfigStruct;
} }

View File

@ -13,6 +13,10 @@ export class HuiRowElementEditor extends HuiElementEditor<LovelaceRowConfig> {
return GENERIC_ROW_TYPE; return GENERIC_ROW_TYPE;
} }
if (this.value?.type === "perform-action") {
return "call-service";
}
return this.value?.type; return this.value?.type;
} }

View File

@ -31,8 +31,9 @@ const actionConfigStructUrl = object({
}); });
const actionConfigStructService = object({ const actionConfigStructService = object({
action: literal("call-service"), action: enums(["call-service", "perform-action"]),
service: string(), service: optional(string()),
perform_action: optional(string()),
service_data: optional(object()), service_data: optional(object()),
data: optional(object()), data: optional(object()),
target: optional( target: optional(
@ -64,6 +65,7 @@ export const actionConfigStructType = object({
"toggle", "toggle",
"more-info", "more-info",
"call-service", "call-service",
"perform-action",
"url", "url",
"navigate", "navigate",
"assist", "assist",
@ -77,6 +79,9 @@ export const actionConfigStruct = dynamic<any>((value) => {
case "call-service": { case "call-service": {
return actionConfigStructService; return actionConfigStructService;
} }
case "perform-action": {
return actionConfigStructService;
}
case "navigate": { case "navigate": {
return actionConfigStructNavigate; return actionConfigStructNavigate;
} }

View File

@ -41,8 +41,12 @@ export interface TextConfig {
text: string; text: string;
} }
export interface CallServiceConfig extends EntityConfig { export interface CallServiceConfig extends EntityConfig {
type: "call-service"; type: "call-service" | "perform-action";
service: string; /** @deprecated use "action" instead */
service?: string;
action: string;
data?: Record<string, any>;
/** @deprecated use "data" instead */
service_data?: Record<string, any>; service_data?: Record<string, any>;
action_name?: string; action_name?: string;
} }

View File

@ -15,15 +15,16 @@ export class HuiCallServiceRow extends HuiButtonRow {
throw new Error("No name specified"); throw new Error("No name specified");
} }
if (!callServiceConfig.service) { if (!callServiceConfig.action && !callServiceConfig.service) {
throw new Error("No service specified"); throw new Error("No action specified");
} }
super.setConfig({ super.setConfig({
tap_action: { tap_action: {
action: "call-service", action: "perform-action",
service: callServiceConfig.service, perform_action: (callServiceConfig.action ||
data: callServiceConfig.service_data, callServiceConfig.service)!,
data: callServiceConfig.data || callServiceConfig.service_data,
}, },
...callServiceConfig, ...callServiceConfig,
type: "button", type: "button",

View File

@ -5672,7 +5672,7 @@
"pipeline_id": "Assistant", "pipeline_id": "Assistant",
"actions": { "actions": {
"default_action": "Default", "default_action": "Default",
"call-service": "Perform action", "perform-action": "Perform action",
"more-info": "More info", "more-info": "More info",
"toggle": "Toggle", "toggle": "Toggle",
"navigate": "Navigate", "navigate": "Navigate",
@ -5787,6 +5787,7 @@
"entity_row": { "entity_row": {
"divider": "Divider", "divider": "Divider",
"call-service": "Perform action", "call-service": "Perform action",
"perform-action": "Perform action",
"section": "Section", "section": "Section",
"weblink": "Web link", "weblink": "Web link",
"attribute": "Attribute", "attribute": "Attribute",