Add configurable actions to Gauge Card (#20833)

* Add actions to gauge card

* struct support

* tap_action in UI

* Apply suggestions from code review

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>

* typo

---------

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
This commit is contained in:
karwosts 2024-05-22 05:10:22 -07:00 committed by GitHub
parent 2113cf5280
commit 9fe8f507ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 59 additions and 5 deletions

View File

@ -8,17 +8,22 @@ import {
nothing, nothing,
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map"; import { styleMap } from "lit/directives/style-map";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import { isValidEntityId } from "../../../common/entity/valid_entity_id"; import { isValidEntityId } from "../../../common/entity/valid_entity_id";
import { getNumberFormatOptions } from "../../../common/number/format_number"; import { getNumberFormatOptions } from "../../../common/number/format_number";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-gauge"; import "../../../components/ha-gauge";
import { UNAVAILABLE } from "../../../data/entity"; import { UNAVAILABLE } from "../../../data/entity";
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
import type { LovelaceCard, LovelaceCardEditor } from "../types"; import type { LovelaceCard, LovelaceCardEditor } from "../types";
@ -123,10 +128,26 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
const name = this._config.name ?? computeStateName(stateObj); const name = this._config.name ?? computeStateName(stateObj);
const hasAnyAction =
!this._config.tap_action ||
hasAction(this._config.tap_action) ||
hasAction(this._config.hold_action) ||
hasAction(this._config.double_tap_action);
// Use `stateObj.state` as value to keep formatting (e.g trailing zeros) // Use `stateObj.state` as value to keep formatting (e.g trailing zeros)
// for consistent value display across gauge, entity, entity-row, etc. // for consistent value display across gauge, entity, entity-row, etc.
return html` return html`
<ha-card @click=${this._handleClick} tabindex="0"> <ha-card
class=${classMap({ action: hasAnyAction })}
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(this._config.hold_action),
hasDoubleClick: hasAction(this._config.double_tap_action),
})}
tabindex=${ifDefined(
hasAction(this._config.tap_action) ? "0" : undefined
)}
>
<ha-gauge <ha-gauge
.min=${this._config.min!} .min=${this._config.min!}
.max=${this._config.max!} .max=${this._config.max!}
@ -256,14 +277,13 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
})); }));
} }
private _handleClick(): void { private _handleAction(ev: ActionHandlerEvent) {
fireEvent(this, "hass-more-info", { entityId: this._config!.entity }); handleAction(this, this.hass!, this._config!, ev.detail.action!);
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return css` return css`
ha-card { ha-card {
cursor: pointer;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
padding: 16px; padding: 16px;
@ -274,6 +294,10 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
box-sizing: border-box; box-sizing: border-box;
} }
ha-card.action {
cursor: pointer;
}
ha-card:focus { ha-card:focus {
outline: none; outline: none;
} }

View File

@ -235,6 +235,9 @@ export interface GaugeCardConfig extends LovelaceCardConfig {
theme?: string; theme?: string;
needle?: boolean; needle?: boolean;
segments?: GaugeSegment[]; segments?: GaugeSegment[];
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
} }
export interface ConfigEntity extends EntityConfig { export interface ConfigEntity extends EntityConfig {

View File

@ -9,6 +9,7 @@ import {
number, number,
object, object,
optional, optional,
refine,
string, string,
} from "superstruct"; } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
@ -17,8 +18,12 @@ import type { SchemaUnion } from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import type { GaugeCardConfig } from "../../cards/types"; import type { GaugeCardConfig } from "../../cards/types";
import type { LovelaceCardEditor } from "../../types"; import type { LovelaceCardEditor } from "../../types";
import { actionConfigStruct } from "../structs/action-struct";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { DEFAULT_MIN, DEFAULT_MAX } from "../../cards/hui-gauge-card"; import { DEFAULT_MIN, DEFAULT_MAX } from "../../cards/hui-gauge-card";
import { UiAction } from "../../components/hui-action-editor";
const TAP_ACTIONS: UiAction[] = ["navigate", "url", "call-service", "none"];
const gaugeSegmentStruct = object({ const gaugeSegmentStruct = object({
from: number(), from: number(),
@ -38,6 +43,13 @@ const cardConfigStruct = assign(
theme: optional(string()), theme: optional(string()),
needle: optional(boolean()), needle: optional(boolean()),
segments: optional(array(gaugeSegmentStruct)), segments: optional(array(gaugeSegmentStruct)),
tap_action: optional(
refine(actionConfigStruct, TAP_ACTIONS.toString(), (value) =>
TAP_ACTIONS.includes(value.action)
)
),
hold_action: optional(actionConfigStruct),
double_tap_action: optional(actionConfigStruct),
}) })
); );
@ -121,6 +133,15 @@ export class HuiGaugeCardEditor
}, },
] as const) ] as const)
: []), : []),
{
name: "tap_action",
selector: {
ui_action: {
actions: TAP_ACTIONS,
default_action: "more-info",
},
},
},
] as const ] as const
); );
@ -210,6 +231,12 @@ export class HuiGaugeCardEditor
return this.hass!.localize( return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.unit" "ui.panel.lovelace.editor.card.generic.unit"
); );
case "tap_action":
return `${this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
default: default:
// "green" | "yellow" | "red" // "green" | "yellow" | "red"
return this.hass!.localize( return this.hass!.localize(