diff --git a/src/panels/lovelace/card-features/hui-switch-toggle-card-feature.ts b/src/panels/lovelace/card-features/hui-switch-toggle-card-feature.ts new file mode 100644 index 0000000000..b316efefad --- /dev/null +++ b/src/panels/lovelace/card-features/hui-switch-toggle-card-feature.ts @@ -0,0 +1,109 @@ +import { mdiPowerOff, mdiPower } from "@mdi/js"; +import type { HassEntity } from "home-assistant-js-websocket"; +import type { TemplateResult } from "lit"; +import { LitElement, html } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { styleMap } from "lit/directives/style-map"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { stateColorCss } from "../../../common/entity/state_color"; +import "../../../components/ha-control-select"; +import type { ControlSelectOption } from "../../../components/ha-control-select"; +import { UNAVAILABLE } from "../../../data/entity"; +import type { HomeAssistant } from "../../../types"; +import type { LovelaceCardFeature } from "../types"; +import { cardFeatureStyles } from "./common/card-feature-styles"; +import type { SwitchToggleCardFeatureConfig } from "./types"; +import { showToast } from "../../../util/toast"; + +export const supportsSwitchToggleCardFeature = (stateObj: HassEntity) => { + const domain = computeDomain(stateObj.entity_id); + return domain === "switch"; +}; + +@customElement("hui-switch-toggle-card-feature") +class HuiSwitchToggleCardFeature + extends LitElement + implements LovelaceCardFeature +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: HassEntity; + + @state() private _config?: SwitchToggleCardFeatureConfig; + + static getStubConfig(): SwitchToggleCardFeatureConfig { + return { + type: "switch-toggle", + }; + } + + public setConfig(config: SwitchToggleCardFeatureConfig): void { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + } + + protected render(): TemplateResult | null { + if ( + !this._config || + !this.hass || + !this.stateObj || + !supportsSwitchToggleCardFeature(this.stateObj) + ) { + return null; + } + + const color = stateColorCss(this.stateObj); + + const options = ["on", "off"].map((entityState) => ({ + value: entityState, + label: this.hass!.formatEntityState(this.stateObj!, entityState), + path: entityState === "on" ? mdiPower : mdiPowerOff, + })); + + return html` + + + `; + } + + private async _valueChanged(ev: CustomEvent) { + const newState = (ev.detail as any).value; + + if (newState === this.stateObj!.state) return; + const service = newState === "on" ? "turn_on" : "turn_off"; + + try { + await this.hass!.callService("switch", service, { + entity_id: this.stateObj!.entity_id, + }); + } catch (_err) { + showToast(this, { + message: this.hass!.localize("ui.notification_toast.action_failed", { + service: "switch." + service, + }), + duration: 5000, + dismissable: true, + }); + } + } + + static styles = cardFeatureStyles; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-switch-toggle-card-feature": HuiSwitchToggleCardFeature; + } +} diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index 24349dfe31..b17bf97c8b 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -88,6 +88,10 @@ export interface SelectOptionsCardFeatureConfig { options?: string[]; } +export interface SwitchToggleCardFeatureConfig { + type: "switch-toggle"; +} + export interface NumericInputCardFeatureConfig { type: "numeric-input"; style?: "buttons" | "slider"; @@ -168,6 +172,7 @@ export type LovelaceCardFeatureConfig = | MediaPlayerVolumeSliderCardFeatureConfig | NumericInputCardFeatureConfig | SelectOptionsCardFeatureConfig + | SwitchToggleCardFeatureConfig | TargetHumidityCardFeatureConfig | TargetTemperatureCardFeatureConfig | UpdateActionsCardFeatureConfig 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 8cad72f8a8..550bfccb0a 100644 --- a/src/panels/lovelace/create-element/create-card-feature-element.ts +++ b/src/panels/lovelace/create-element/create-card-feature-element.ts @@ -20,6 +20,7 @@ import "../card-features/hui-lock-open-door-card-feature"; import "../card-features/hui-media-player-volume-slider-card-feature"; import "../card-features/hui-numeric-input-card-feature"; import "../card-features/hui-select-options-card-feature"; +import "../card-features/hui-switch-toggle-card-feature"; import "../card-features/hui-target-temperature-card-feature"; import "../card-features/hui-target-humidity-card-feature"; import "../card-features/hui-update-actions-card-feature"; @@ -55,6 +56,7 @@ const TYPES = new Set([ "media-player-volume-slider", "numeric-input", "select-options", + "switch-toggle", "target-humidity", "target-temperature", "update-actions", 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 32170317b1..a8008d7148 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 @@ -40,6 +40,7 @@ import { supportsLockOpenDoorCardFeature } from "../../card-features/hui-lock-op import { supportsMediaPlayerVolumeSliderCardFeature } from "../../card-features/hui-media-player-volume-slider-card-feature"; import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric-input-card-feature"; import { supportsSelectOptionsCardFeature } from "../../card-features/hui-select-options-card-feature"; +import { supportsSwitchToggleCardFeature } from "../../card-features/hui-switch-toggle-card-feature"; import { supportsTargetHumidityCardFeature } from "../../card-features/hui-target-humidity-card-feature"; import { supportsTargetTemperatureCardFeature } from "../../card-features/hui-target-temperature-card-feature"; import { supportsUpdateActionsCardFeature } from "../../card-features/hui-update-actions-card-feature"; @@ -74,6 +75,7 @@ const UI_FEATURE_TYPES = [ "media-player-volume-slider", "numeric-input", "select-options", + "switch-toggle", "target-humidity", "target-temperature", "update-actions", @@ -132,6 +134,7 @@ const SUPPORTS_FEATURE_TYPES: Record< "update-actions": supportsUpdateActionsCardFeature, "vacuum-commands": supportsVacuumCommandsCardFeature, "water-heater-operation-modes": supportsWaterHeaterOperationModesCardFeature, + "switch-toggle": supportsSwitchToggleCardFeature, }; const customCardFeatures = getCustomCardFeatures(); diff --git a/src/translations/en.json b/src/translations/en.json index e04de83107..279a57487e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -7343,6 +7343,9 @@ "options": "Options", "customize_options": "Customize options" }, + "switch-toggle": { + "label": "Switch toggle" + }, "numeric-input": { "label": "Numeric input", "style": "Style",