diff --git a/src/panels/lovelace/card-features/hui-button-card-feature.ts b/src/panels/lovelace/card-features/hui-button-card-feature.ts new file mode 100644 index 0000000000..5d1f08ff2a --- /dev/null +++ b/src/panels/lovelace/card-features/hui-button-card-feature.ts @@ -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` + + + ${this._config.action_name ?? + this.hass.localize("ui.card.button.press")} + + + `; + } + + static styles = cardFeatureStyles; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-button-card-feature": HuiButtonCardFeature; + } +} diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index 9ded542fe3..c937170aff 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -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 diff --git a/src/panels/lovelace/create-element/create-card-feature-element.ts b/src/panels/lovelace/create-element/create-card-feature-element.ts index fc003da1f9..fa5a006b44 100644 --- a/src/panels/lovelace/create-element/create-card-feature-element.ts +++ b/src/panels/lovelace/create-element/create-card-feature-element.ts @@ -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([ "alarm-modes", + "button", "area-controls", "climate-fan-modes", "climate-swing-modes", diff --git a/src/panels/lovelace/editor/config-elements/hui-button-card-feature-editor.ts b/src/panels/lovelace/editor/config-elements/hui-button-card-feature-editor.ts new file mode 100644 index 0000000000..e5fa06a69b --- /dev/null +++ b/src/panels/lovelace/editor/config-elements/hui-button-card-feature-editor.ts @@ -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` + + `; + } + + 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; + } +} diff --git a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts index befbb73c7b..0b0c17f27b 100644 --- a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts @@ -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([ "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":