mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 19:26:36 +00:00
Add tile card feature for counter actions (#24340)
* Add tile card feature for counter actions * Format * Change icon * Disable buttons when hit limit * Change increment/decrement icons
This commit is contained in:
parent
0bd7d27c57
commit
a7a4194e09
@ -0,0 +1,134 @@
|
||||
import { mdiRestore, mdiPlus, mdiMinus } 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 { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import "../../../components/ha-control-select";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import { COUNTER_ACTIONS, type CounterActionsCardFeatureConfig } from "./types";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-control-button";
|
||||
|
||||
export const supportsCounterActionsCardFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "counter";
|
||||
};
|
||||
|
||||
interface CounterButton {
|
||||
translationKey: string;
|
||||
icon: string;
|
||||
serviceName: string;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
export const COUNTER_ACTIONS_BUTTON: Record<
|
||||
string,
|
||||
(stateObj: HassEntity) => CounterButton
|
||||
> = {
|
||||
increment: (stateObj) => ({
|
||||
translationKey: "increment",
|
||||
icon: mdiPlus,
|
||||
serviceName: "increment",
|
||||
disabled: parseInt(stateObj.state) === stateObj.attributes.maximum,
|
||||
}),
|
||||
reset: () => ({
|
||||
translationKey: "reset",
|
||||
icon: mdiRestore,
|
||||
serviceName: "reset",
|
||||
disabled: false,
|
||||
}),
|
||||
decrement: (stateObj) => ({
|
||||
translationKey: "decrement",
|
||||
icon: mdiMinus,
|
||||
serviceName: "decrement",
|
||||
disabled: parseInt(stateObj.state) === stateObj.attributes.minimum,
|
||||
}),
|
||||
};
|
||||
|
||||
@customElement("hui-counter-actions-card-feature")
|
||||
class HuiCounterActionsCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
|
||||
@state() private _config?: CounterActionsCardFeatureConfig;
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/hui-counter-actions-card-feature-editor"
|
||||
);
|
||||
return document.createElement("hui-counter-actions-card-feature-editor");
|
||||
}
|
||||
|
||||
static getStubConfig(): CounterActionsCardFeatureConfig {
|
||||
return {
|
||||
type: "counter-actions",
|
||||
actions: COUNTER_ACTIONS.map((action) => action),
|
||||
};
|
||||
}
|
||||
|
||||
public setConfig(config: CounterActionsCardFeatureConfig): void {
|
||||
if (!config) {
|
||||
throw new Error("Invalid configuration");
|
||||
}
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsCounterActionsCardFeature(this.stateObj)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-control-button-group>
|
||||
${this._config?.actions
|
||||
?.filter((action) => COUNTER_ACTIONS.includes(action))
|
||||
.map((action) => {
|
||||
const button = COUNTER_ACTIONS_BUTTON[action](this.stateObj!);
|
||||
return html`
|
||||
<ha-control-button
|
||||
.entry=${button}
|
||||
.label=${this.hass!.localize(
|
||||
// @ts-ignore
|
||||
`ui.card.counter.actions.${button.translationKey}`
|
||||
)}
|
||||
@click=${this._onActionTap}
|
||||
.disabled=${button.disabled ||
|
||||
this.stateObj?.state === UNAVAILABLE}
|
||||
>
|
||||
<ha-svg-icon .path=${button.icon}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
`;
|
||||
})}
|
||||
</ha-control-button-group>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onActionTap(ev): void {
|
||||
ev.stopPropagation();
|
||||
const entry = (ev.target! as any).entry as CounterButton;
|
||||
this.hass!.callService("counter", entry.serviceName, {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
static styles = cardFeatureStyles;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-counter-actions-card-feature": HuiCounterActionsCardFeature;
|
||||
}
|
||||
}
|
@ -83,6 +83,15 @@ export interface ClimatePresetModesCardFeatureConfig {
|
||||
preset_modes?: string[];
|
||||
}
|
||||
|
||||
export const COUNTER_ACTIONS = ["increment", "reset", "decrement"] as const;
|
||||
|
||||
export type CounterActions = (typeof COUNTER_ACTIONS)[number];
|
||||
|
||||
export interface CounterActionsCardFeatureConfig {
|
||||
type: "counter-actions";
|
||||
actions?: CounterActions[];
|
||||
}
|
||||
|
||||
export interface SelectOptionsCardFeatureConfig {
|
||||
type: "select-options";
|
||||
options?: string[];
|
||||
@ -156,6 +165,7 @@ export type LovelaceCardFeatureConfig =
|
||||
| ClimateSwingHorizontalModesCardFeatureConfig
|
||||
| ClimateHvacModesCardFeatureConfig
|
||||
| ClimatePresetModesCardFeatureConfig
|
||||
| CounterActionsCardFeatureConfig
|
||||
| CoverOpenCloseCardFeatureConfig
|
||||
| CoverPositionCardFeatureConfig
|
||||
| CoverTiltPositionCardFeatureConfig
|
||||
|
@ -4,6 +4,7 @@ import "../card-features/hui-climate-swing-modes-card-feature";
|
||||
import "../card-features/hui-climate-swing-horizontal-modes-card-feature";
|
||||
import "../card-features/hui-climate-hvac-modes-card-feature";
|
||||
import "../card-features/hui-climate-preset-modes-card-feature";
|
||||
import "../card-features/hui-counter-actions-card-feature";
|
||||
import "../card-features/hui-cover-open-close-card-feature";
|
||||
import "../card-features/hui-cover-position-card-feature";
|
||||
import "../card-features/hui-cover-tilt-card-feature";
|
||||
@ -40,6 +41,7 @@ const TYPES = new Set<LovelaceCardFeatureConfig["type"]>([
|
||||
"climate-swing-horizontal-modes",
|
||||
"climate-hvac-modes",
|
||||
"climate-preset-modes",
|
||||
"counter-actions",
|
||||
"cover-open-close",
|
||||
"cover-position",
|
||||
"cover-tilt-position",
|
||||
|
@ -24,6 +24,7 @@ import { supportsClimateHvacModesCardFeature } from "../../card-features/hui-cli
|
||||
import { supportsClimatePresetModesCardFeature } from "../../card-features/hui-climate-preset-modes-card-feature";
|
||||
import { supportsClimateSwingModesCardFeature } from "../../card-features/hui-climate-swing-modes-card-feature";
|
||||
import { supportsClimateSwingHorizontalModesCardFeature } from "../../card-features/hui-climate-swing-horizontal-modes-card-feature";
|
||||
import { supportsCounterActionsCardFeature } from "../../card-features/hui-counter-actions-card-feature";
|
||||
import { supportsCoverOpenCloseCardFeature } from "../../card-features/hui-cover-open-close-card-feature";
|
||||
import { supportsCoverPositionCardFeature } from "../../card-features/hui-cover-position-card-feature";
|
||||
import { supportsCoverTiltCardFeature } from "../../card-features/hui-cover-tilt-card-feature";
|
||||
@ -59,6 +60,7 @@ const UI_FEATURE_TYPES = [
|
||||
"climate-preset-modes",
|
||||
"climate-swing-modes",
|
||||
"climate-swing-horizontal-modes",
|
||||
"counter-actions",
|
||||
"cover-open-close",
|
||||
"cover-position",
|
||||
"cover-tilt-position",
|
||||
@ -92,6 +94,7 @@ const EDITABLES_FEATURE_TYPES = new Set<UiFeatureTypes>([
|
||||
"climate-preset-modes",
|
||||
"climate-swing-modes",
|
||||
"climate-swing-horizontal-modes",
|
||||
"counter-actions",
|
||||
"fan-preset-modes",
|
||||
"humidifier-modes",
|
||||
"lawn-mower-commands",
|
||||
@ -113,6 +116,7 @@ const SUPPORTS_FEATURE_TYPES: Record<
|
||||
supportsClimateSwingHorizontalModesCardFeature,
|
||||
"climate-hvac-modes": supportsClimateHvacModesCardFeature,
|
||||
"climate-preset-modes": supportsClimatePresetModesCardFeature,
|
||||
"counter-actions": supportsCounterActionsCardFeature,
|
||||
"cover-open-close": supportsCoverOpenCloseCardFeature,
|
||||
"cover-position": supportsCoverPositionCardFeature,
|
||||
"cover-tilt-position": supportsCoverTiltPositionCardFeature,
|
||||
|
@ -0,0 +1,91 @@
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import {
|
||||
COUNTER_ACTIONS,
|
||||
type LovelaceCardFeatureContext,
|
||||
type CounterActionsCardFeatureConfig,
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
|
||||
@customElement("hui-counter-actions-card-feature-editor")
|
||||
export class HuiCounterActionsCardFeatureEditor
|
||||
extends LitElement
|
||||
implements LovelaceCardFeatureEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: CounterActionsCardFeatureConfig;
|
||||
|
||||
public setConfig(config: CounterActionsCardFeatureConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc) =>
|
||||
[
|
||||
{
|
||||
name: "actions",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list",
|
||||
reorder: true,
|
||||
options: COUNTER_ACTIONS.map((action) => ({
|
||||
value: action,
|
||||
label: `${localize(
|
||||
`ui.panel.lovelace.editor.features.types.counter-actions.actions.${action}`
|
||||
)}`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const schema = this._schema(this.hass.localize);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
default:
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-counter-actions-card-feature-editor": HuiCounterActionsCardFeatureEditor;
|
||||
}
|
||||
}
|
@ -7001,7 +7001,8 @@
|
||||
"suggested_cards": "Suggested cards",
|
||||
"other_cards": "Other cards",
|
||||
"custom_cards": "Custom cards",
|
||||
"features": "Features"
|
||||
"features": "Features",
|
||||
"actions": "Actions"
|
||||
},
|
||||
"heading": {
|
||||
"name": "Heading",
|
||||
@ -7320,6 +7321,14 @@
|
||||
"customize_modes": "Customize preset modes",
|
||||
"preset_modes": "Preset modes"
|
||||
},
|
||||
"counter-actions": {
|
||||
"label": "Counter actions",
|
||||
"actions": {
|
||||
"increment": "Increment",
|
||||
"decrement": "Decrement",
|
||||
"reset": "Reset"
|
||||
}
|
||||
},
|
||||
"fan-preset-modes": {
|
||||
"label": "Fan preset modes",
|
||||
"style": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style%]",
|
||||
|
Loading…
x
Reference in New Issue
Block a user