Skip to content

Commit 8006050

Browse files
committed
Keep a single generic alert card
1 parent 56a8bd1 commit 8006050

14 files changed

Lines changed: 319 additions & 1387 deletions
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import { consume, type ContextType } from "@lit/context";
2+
import { css, html, LitElement, nothing } from "lit";
3+
import { customElement, property, state } from "lit/decorators";
4+
import { classMap } from "lit/directives/class-map";
5+
import { styleMap } from "lit/directives/style-map";
6+
import { computeCssColor } from "../../../common/color/compute-color";
7+
import { fireEvent } from "../../../common/dom/fire_event";
8+
import { computeStateName } from "../../../common/entity/compute_state_name";
9+
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
10+
import "../../../components/ha-card";
11+
import "../../../components/ha-relative-time";
12+
import "../../../components/ha-state-icon";
13+
import "../../../components/tile/ha-tile-container";
14+
import "../../../components/tile/ha-tile-icon";
15+
import "../../../components/tile/ha-tile-info";
16+
import { formattersContext } from "../../../data/context";
17+
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
18+
import type { HomeAssistant } from "../../../types";
19+
import type { LovelaceCard, LovelaceGridOptions } from "../types";
20+
import { tileCardStyle } from "./tile/tile-card-style";
21+
import type { AlertCardConfig } from "./types";
22+
23+
@customElement("hui-alert-card")
24+
export class HuiAlertCard extends LitElement implements LovelaceCard {
25+
@property({ attribute: false }) public hass!: HomeAssistant;
26+
27+
@state() private _config?: AlertCardConfig;
28+
29+
@state()
30+
@consume({ context: formattersContext, subscribe: true })
31+
private _formatters!: ContextType<typeof formattersContext>;
32+
33+
public setConfig(config: AlertCardConfig): void {
34+
if (
35+
!isValidEntityId(config.entity) ||
36+
(config.color !== undefined && typeof config.color !== "string") ||
37+
(config.pulse !== undefined && typeof config.pulse !== "boolean")
38+
) {
39+
throw new Error("Invalid configuration");
40+
}
41+
this._config = config;
42+
}
43+
44+
public getCardSize(): number {
45+
return 1;
46+
}
47+
48+
public getGridOptions(): LovelaceGridOptions {
49+
return {
50+
columns: 6,
51+
rows: 1,
52+
min_columns: 6,
53+
min_rows: 1,
54+
};
55+
}
56+
57+
protected render() {
58+
if (!this._config || !this.hass || !this._formatters) {
59+
return nothing;
60+
}
61+
62+
const stateObj = this.hass.states[this._config.entity];
63+
if (!stateObj) {
64+
return nothing;
65+
}
66+
67+
const stateDisplay = this._formatters.formatEntityState(stateObj);
68+
const pulse = this._config.pulse !== false;
69+
const hasColor =
70+
this._config.color !== undefined && this._config.color !== "none";
71+
72+
return html`
73+
<ha-card
74+
class=${classMap({ pulse, "no-color": !hasColor })}
75+
style=${styleMap({
76+
"--ha-alert-color":
77+
this._config.color && hasColor
78+
? computeCssColor(this._config.color)
79+
: undefined,
80+
"--ha-alert-static-opacity": pulse
81+
? undefined
82+
: "var(--ha-alert-pulse-opacity)",
83+
})}
84+
>
85+
<ha-tile-container
86+
.interactive=${true}
87+
.actionHandlerOptions=${{ hasHold: false, hasDoubleClick: false }}
88+
@action=${this._handleAction}
89+
>
90+
<ha-tile-icon slot="icon">
91+
<ha-state-icon slot="icon" .stateObj=${stateObj}></ha-state-icon>
92+
</ha-tile-icon>
93+
<ha-tile-info slot="info">
94+
<span slot="primary">${computeStateName(stateObj)}</span>
95+
<span slot="secondary">
96+
${stateDisplay} ·
97+
<ha-relative-time
98+
.datetime=${stateObj.last_changed}
99+
></ha-relative-time>
100+
</span>
101+
</ha-tile-info>
102+
</ha-tile-container>
103+
</ha-card>
104+
`;
105+
}
106+
107+
private _handleAction(ev: ActionHandlerEvent): void {
108+
if (ev.detail.action === "tap" && this._config) {
109+
fireEvent(this, "hass-more-info", { entityId: this._config.entity });
110+
}
111+
}
112+
113+
static styles = [
114+
tileCardStyle,
115+
css`
116+
@keyframes pulse-opacity {
117+
from {
118+
opacity: 0;
119+
}
120+
to {
121+
opacity: var(--ha-pulse-opacity, 0.3);
122+
}
123+
}
124+
:host {
125+
display: block;
126+
--ha-alert-pulse-duration: 1s;
127+
--ha-alert-pulse-opacity: 0.3;
128+
--ha-alert-static-opacity: 0;
129+
}
130+
ha-card {
131+
position: relative;
132+
overflow: hidden;
133+
height: 100%;
134+
}
135+
ha-card:not(.no-color) {
136+
--tile-color: var(--ha-alert-color);
137+
}
138+
ha-card.no-color {
139+
--tile-color: var(--secondary-text-color);
140+
}
141+
ha-card::before {
142+
position: absolute;
143+
inset: 0;
144+
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
145+
background-color: var(--ha-alert-color);
146+
content: "";
147+
opacity: var(--ha-alert-static-opacity);
148+
pointer-events: none;
149+
}
150+
ha-card.pulse::before {
151+
--ha-pulse-opacity: var(--ha-alert-pulse-opacity);
152+
animation: pulse-opacity var(--ha-alert-pulse-duration) ease-in-out
153+
infinite alternate;
154+
}
155+
ha-card:not(.pulse)::before {
156+
animation: none;
157+
}
158+
ha-tile-container {
159+
position: relative;
160+
}
161+
@media (prefers-reduced-motion: reduce) {
162+
ha-card::before {
163+
animation: none;
164+
opacity: var(--ha-alert-pulse-opacity);
165+
}
166+
}
167+
`,
168+
];
169+
}
170+
171+
declare global {
172+
interface HTMLElementTagNameMap {
173+
"hui-alert-card": HuiAlertCard;
174+
}
175+
}

src/panels/lovelace/cards/security-alerts/context.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/panels/lovelace/cards/security-alerts/helpers.ts

Lines changed: 0 additions & 203 deletions
This file was deleted.

0 commit comments

Comments
 (0)