Add new card feature: button (#26165)

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
This commit is contained in:
Douwe 2025-07-30 16:13:05 +02:00 committed by GitHub
parent b355556c07
commit 6dbfc2f4ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 170 additions and 0 deletions

View File

@ -0,0 +1,98 @@
import { html, LitElement, nothing } from "lit";
import type { HassEntity } from "home-assistant-js-websocket";
import { customElement, property, state } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import "../../../components/ha-control-button";
import "../../../components/ha-control-button-group";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature } from "../types";
import { cardFeatureStyles } from "./common/card-feature-styles";
import type {
LovelaceCardFeatureContext,
ButtonCardFeatureConfig,
} from "./types";
export const supportsButtonCardFeature = (
hass: HomeAssistant,
context: LovelaceCardFeatureContext
) => {
const stateObj = context.entity_id
? hass.states[context.entity_id]
: undefined;
if (!stateObj) return false;
const domain = computeDomain(stateObj.entity_id);
return ["button", "script"].includes(domain);
};
@customElement("hui-button-card-feature")
class HuiButtonCardFeature extends LitElement implements LovelaceCardFeature {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
@state() private _config?: ButtonCardFeatureConfig;
private get _stateObj() {
if (!this.hass || !this.context || !this.context.entity_id) {
return undefined;
}
return this.hass.states[this.context.entity_id!] as HassEntity | undefined;
}
private _pressButton() {
if (!this.hass || !this._stateObj) return;
const domain = computeDomain(this._stateObj.entity_id);
const service = domain === "button" ? "press" : "turn_on";
this.hass.callService(domain, service, {
entity_id: this._stateObj.entity_id,
});
}
static getStubConfig(): ButtonCardFeatureConfig {
return {
type: "button",
};
}
public setConfig(config: ButtonCardFeatureConfig): void {
if (!config) {
throw new Error("Invalid configuration");
}
this._config = config;
}
protected render() {
if (
!this._config ||
!this.hass ||
!this.context ||
!this._stateObj ||
!supportsButtonCardFeature(this.hass, this.context)
) {
return nothing;
}
return html`
<ha-control-button-group>
<ha-control-button
.disabled=${["unavailable", "unknown"].includes(this._stateObj.state)}
class="press-button"
@click=${this._pressButton}
>
${this._config.action_name ??
this.hass.localize("ui.card.button.press")}
</ha-control-button>
</ha-control-button-group>
`;
}
static styles = cardFeatureStyles;
}
declare global {
interface HTMLElementTagNameMap {
"hui-button-card-feature": HuiButtonCardFeature;
}
}

View File

@ -2,6 +2,11 @@ import type { AlarmMode } from "../../../data/alarm_control_panel";
import type { HvacMode } from "../../../data/climate";
import type { OperationMode } from "../../../data/water_heater";
export interface ButtonCardFeatureConfig {
type: "button";
action_name?: string;
}
export interface CoverOpenCloseCardFeatureConfig {
type: "cover-open-close";
}
@ -185,6 +190,7 @@ export type LovelaceCardFeaturePosition = "bottom" | "inline";
export type LovelaceCardFeatureConfig =
| AlarmModesCardFeatureConfig
| ButtonCardFeatureConfig
| ClimateFanModesCardFeatureConfig
| ClimateSwingModesCardFeatureConfig
| ClimateSwingHorizontalModesCardFeatureConfig

View File

@ -1,4 +1,5 @@
import "../card-features/hui-alarm-modes-card-feature";
import "../card-features/hui-button-card-feature";
import "../card-features/hui-climate-fan-modes-card-feature";
import "../card-features/hui-climate-hvac-modes-card-feature";
import "../card-features/hui-climate-preset-modes-card-feature";
@ -37,6 +38,7 @@ import {
const TYPES = new Set<LovelaceCardFeatureConfig["type"]>([
"alarm-modes",
"button",
"area-controls",
"climate-fan-modes",
"climate-swing-modes",

View File

@ -0,0 +1,60 @@
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { HomeAssistant } from "../../../../types";
import type { ButtonCardFeatureConfig } from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
import "../../../../components/ha-form/ha-form";
import type { HaFormSchema } from "../../../../components/ha-form/types";
@customElement("hui-button-card-feature-editor")
export class HuiButtonCardFeatureEditor
extends LitElement
implements LovelaceCardFeatureEditor
{
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _config?: ButtonCardFeatureConfig;
public setConfig(config: ButtonCardFeatureConfig): void {
this._config = config;
}
private _schema: HaFormSchema[] = [
{
name: "action_name",
selector: {
text: {},
},
},
];
protected render() {
if (!this.hass || !this._config) {
return nothing;
}
return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.schema=${this._schema}
@value-changed=${this._valueChanged}
></ha-form>
`;
}
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
this.dispatchEvent(
new CustomEvent("config-changed", {
detail: { config: ev.detail.value },
})
);
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-button-card-feature-editor": HuiButtonCardFeatureEditor;
}
}

View File

@ -19,6 +19,7 @@ import {
import type { HomeAssistant } from "../../../../types";
import { supportsAlarmModesCardFeature } from "../../card-features/hui-alarm-modes-card-feature";
import { supportsAreaControlsCardFeature } from "../../card-features/hui-area-controls-card-feature";
import { supportsButtonCardFeature } from "../../card-features/hui-button-card-feature";
import { supportsClimateFanModesCardFeature } from "../../card-features/hui-climate-fan-modes-card-feature";
import { supportsClimateHvacModesCardFeature } from "../../card-features/hui-climate-hvac-modes-card-feature";
import { supportsClimatePresetModesCardFeature } from "../../card-features/hui-climate-preset-modes-card-feature";
@ -63,6 +64,7 @@ type SupportsFeature = (
const UI_FEATURE_TYPES = [
"alarm-modes",
"area-controls",
"button",
"climate-fan-modes",
"climate-hvac-modes",
"climate-preset-modes",
@ -98,6 +100,7 @@ type UiFeatureTypes = (typeof UI_FEATURE_TYPES)[number];
const EDITABLES_FEATURE_TYPES = new Set<UiFeatureTypes>([
"alarm-modes",
"area-controls",
"button",
"climate-fan-modes",
"climate-hvac-modes",
"climate-preset-modes",
@ -120,6 +123,7 @@ const SUPPORTS_FEATURE_TYPES: Record<
> = {
"alarm-modes": supportsAlarmModesCardFeature,
"area-controls": supportsAreaControlsCardFeature,
button: supportsButtonCardFeature,
"climate-fan-modes": supportsClimateFanModesCardFeature,
"climate-swing-modes": supportsClimateSwingModesCardFeature,
"climate-swing-horizontal-modes":