Compare commits

...
Author SHA1 Message Date
Maarten Lakerveld 7cf50a001b Drop hass from the clock, glance and other simple cards
The clock cards consume locale and the server time zone from context,
and the date format helper takes those values instead of hass. The
glance card consumes its entity states and registry entries through the
entity helpers, so it only rerenders when a shown entity changes. The
iframe, empty state, starting and toggle group cards consume localize,
config, states and api, and dispatch actions as hass-action events.
2026-10-27 12:48:19 +01:00
9 changed files with 275 additions and 124 deletions

No files matched your search

@@ -1,5 +1,5 @@
import { resolveTimeZone } from "../../../../common/datetime/resolve-time-zone";
import type { HomeAssistant } from "../../../../types";
import type { FrontendLocaleData } from "../../../../data/translation";
import type { ClockCardConfig, ClockCardDatePart } from "../types";
type ClockCardSeparatorPart = Extract<
@@ -17,21 +17,22 @@ interface ClockCardDateConfig {
}
/**
* Resolves the locale and time zone for a clock card from `hass` and the
* card's configuration. Applies the optional `time_format` override to the
* locale and falls back to the user's preferred time zone.
* Resolves the locale and time zone for a clock card from the user's locale,
* the server time zone and the card's configuration. Applies the optional
* `time_format` override to the locale and falls back to the user's preferred
* time zone.
*/
export const resolveClockCardLocale = (
hass: HomeAssistant,
userLocale: FrontendLocaleData,
serverTimeZone: string,
config: Pick<ClockCardConfig, "time_format" | "time_zone">
): { locale: HomeAssistant["locale"]; timeZone: string } => {
): { locale: FrontendLocaleData; timeZone: string } => {
const locale = config.time_format
? { ...hass.locale, time_format: config.time_format }
: hass.locale;
? { ...userLocale, time_format: config.time_format }
: userLocale;
const timeZone =
config.time_zone ||
resolveTimeZone(locale.time_zone, hass.config?.time_zone);
config.time_zone || resolveTimeZone(locale.time_zone, serverTimeZone);
return { locale, timeZone };
};
@@ -5,7 +5,17 @@ import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import "../../../../components/ha-marquee-text";
import type { HomeAssistant } from "../../../../types";
import { consume } from "../../../../common/decorators/consume";
import { transform } from "../../../../common/decorators/transform";
import {
configContext,
internationalizationContext,
} from "../../../../data/context";
import type { FrontendLocaleData } from "../../../../data/translation";
import type {
HomeAssistantConfig,
HomeAssistantInternationalization,
} from "../../../../types";
import type { ClockCardConfig } from "../types";
import {
formatClockCardDate,
@@ -41,10 +51,22 @@ const MINUTE_TICKS = Array.from({ length: 60 }, (_, i) => i);
@customElement("hui-clock-card-analog")
export class HuiClockCardAnalog extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public config?: ClockCardConfig;
@state()
@consume({ context: internationalizationContext, subscribe: true })
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
transformer: ({ locale }) => locale,
})
private _locale?: FrontendLocaleData;
@state()
@consume({ context: configContext, subscribe: true })
@transform<HomeAssistantConfig, string>({
transformer: ({ config }) => config.time_zone,
})
private _serverTimeZone?: string;
@state() private _dateTimeFormat?: Intl.DateTimeFormat;
@state() private _hourOffsetSec?: number;
@@ -79,15 +101,12 @@ export class HuiClockCardAnalog extends LitElement {
}
protected updated(changedProps: PropertyValues) {
if (changedProps.has("config") || changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (
changedProps.has("config") ||
!oldHass ||
oldHass.locale !== this.hass?.locale
) {
this._initDate();
}
if (
changedProps.has("config") ||
changedProps.has("_locale") ||
changedProps.has("_serverTimeZone")
) {
this._initDate();
}
if (
@@ -120,18 +139,22 @@ export class HuiClockCardAnalog extends LitElement {
};
private _initDate() {
if (!this.config || !this.hass) {
if (!this.config || !this._locale || this._serverTimeZone === undefined) {
this._stopDateTick();
this._date = undefined;
return;
}
const { timeZone } = resolveClockCardLocale(this.hass, this.config);
const { locale, timeZone } = resolveClockCardLocale(
this._locale,
this._serverTimeZone,
this.config
);
this._language = this.hass.locale.language;
this._language = locale.language;
this._timeZone = timeZone;
this._dateTimeFormat = new Intl.DateTimeFormat(this.hass.locale.language, {
this._dateTimeFormat = new Intl.DateTimeFormat(locale.language, {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
@@ -3,8 +3,18 @@ import type { PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../components/ha-marquee-text";
import type { ClockCardConfig } from "../types";
import type { HomeAssistant } from "../../../../types";
import type {
HomeAssistantConfig,
HomeAssistantInternationalization,
} from "../../../../types";
import { useAmPm } from "../../../../common/datetime/use_am_pm";
import { consume } from "../../../../common/decorators/consume";
import { transform } from "../../../../common/decorators/transform";
import {
configContext,
internationalizationContext,
} from "../../../../data/context";
import type { FrontendLocaleData } from "../../../../data/translation";
import {
formatClockCardDate,
getClockCardDateConfig,
@@ -16,10 +26,22 @@ const INTERVAL = 1000;
@customElement("hui-clock-card-digital")
export class HuiClockCardDigital extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public config?: ClockCardConfig;
@state()
@consume({ context: internationalizationContext, subscribe: true })
@transform<HomeAssistantInternationalization, FrontendLocaleData>({
transformer: ({ locale }) => locale,
})
private _locale?: FrontendLocaleData;
@state()
@consume({ context: configContext, subscribe: true })
@transform<HomeAssistantConfig, string>({
transformer: ({ config }) => config.time_zone,
})
private _serverTimeZone?: string;
@state() private _dateTimeFormat?: Intl.DateTimeFormat;
@state() private _timeHour?: string;
@@ -41,19 +63,23 @@ export class HuiClockCardDigital extends LitElement {
private _language?: string;
private _initDate() {
if (!this.config || !this.hass) {
if (!this.config || !this._locale || this._serverTimeZone === undefined) {
this._date = undefined;
this._lastDateMinute = undefined;
return;
}
const { locale, timeZone } = resolveClockCardLocale(this.hass, this.config);
const { locale, timeZone } = resolveClockCardLocale(
this._locale,
this._serverTimeZone,
this.config
);
const h12 = useAmPm(locale);
this._language = this.hass.locale.language;
this._language = locale.language;
this._timeZone = timeZone;
this._dateTimeFormat = new Intl.DateTimeFormat(this.hass.locale.language, {
this._dateTimeFormat = new Intl.DateTimeFormat(locale.language, {
hour: h12 ? "numeric" : "2-digit",
minute: "2-digit",
second: "2-digit",
@@ -66,16 +92,13 @@ export class HuiClockCardDigital extends LitElement {
this._tick();
}
protected updated(changedProps: PropertyValues<this>) {
if (changedProps.has("config") || changedProps.has("hass")) {
const oldHass = changedProps.get("hass");
if (
changedProps.has("config") ||
!oldHass ||
oldHass.locale !== this.hass?.locale
) {
this._initDate();
}
protected updated(changedProps: PropertyValues) {
if (
changedProps.has("config") ||
changedProps.has("_locale") ||
changedProps.has("_serverTimeZone")
) {
this._initDate();
}
}
+1 -6
View File
@@ -1,9 +1,8 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import "../../../components/ha-alert";
import "../../../components/ha-card";
import type { HomeAssistant } from "../../../types";
import type {
LovelaceCard,
LovelaceCardEditor,
@@ -24,8 +23,6 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
};
}
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _config?: ClockCardConfig;
public setConfig(config: ClockCardConfig): void {
@@ -122,13 +119,11 @@ export class HuiClockCard extends LitElement implements LovelaceCard {
this._config.clock_style === "analog"
? html`
<hui-clock-card-analog
.hass=${this.hass}
.config=${this._config}
></hui-clock-card-analog>
`
: html`
<hui-clock-card-digital
.hass=${this.hass}
.config=${this._config}
></hui-clock-card-digital>
`
@@ -1,14 +1,13 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { ifDefined } from "lit/directives/if-defined";
import { computeCssColor } from "../../../common/color/compute-color";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-card";
import "../../../components/ha-button";
import "../../../components/ha-icon";
import type { HomeAssistant } from "../../../types";
import { handleAction } from "../common/handle-action";
import type { LovelaceCard, LovelaceCardEditor } from "../types";
import type { EmptyStateCardConfig } from "./types";
@@ -27,8 +26,6 @@ export class HuiEmptyStateCard extends LitElement implements LovelaceCard {
};
}
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _config?: EmptyStateCardConfig;
public getCardSize(): number {
@@ -40,7 +37,7 @@ export class HuiEmptyStateCard extends LitElement implements LovelaceCard {
}
protected render() {
if (!this.hass || !this._config) {
if (!this._config) {
return nothing;
}
@@ -108,8 +105,11 @@ export class HuiEmptyStateCard extends LitElement implements LovelaceCard {
private _handleButtonAction(ev: Event): void {
const index = (ev.currentTarget as any).index;
const button = this._config?.buttons?.[index];
if (this.hass && button) {
handleAction(this, this.hass, { tap_action: button.tap_action }, "tap");
if (button) {
fireEvent(this, "hass-action", {
config: { tap_action: button.tap_action },
action: "tap",
});
}
}
+111 -29
View File
@@ -1,16 +1,32 @@
import type { HassConfig, HassEntity } from "home-assistant-js-websocket";
import type { PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { consume } from "../../../common/decorators/consume";
import {
consumeEntityStates,
consumeLocalize,
} from "../../../common/decorators/consume-context-entry";
import { transform } from "../../../common/decorators/transform";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import type { HASSDomCurrentTargetEvent } from "../../../common/dom/fire_event";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeDomain } from "../../../common/entity/compute_domain";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/entity/state-badge";
import "../../../components/ha-card";
import "../../../components/ha-icon";
import "../../../components/ha-relative-time";
import {
configContext,
entitiesContext,
formattersContext,
uiContext,
} from "../../../data/context";
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity/entity";
import type { EntityRegistryDisplayEntry } from "../../../data/entity/entity_registry";
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type {
CallServiceActionConfig,
@@ -20,12 +36,16 @@ import {
SENSOR_DEVICE_CLASS_UPTIME,
SENSOR_TIMESTAMP_DEVICE_CLASSES,
} from "../../../data/sensor";
import type { HomeAssistant } from "../../../types";
import type { Themes } from "../../../data/ws-themes";
import type {
HomeAssistant,
HomeAssistantConfig,
HomeAssistantFormatters,
HomeAssistantUI,
} from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action";
import { hasAction, hasAnyAction } from "../common/has-action";
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
import { applyDefaultColor } from "../common/entity-color-config";
import { processConfigEntities } from "../common/process-config-entities";
import "../components/hui-timestamp-display";
@@ -63,10 +83,49 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
return { type: "glance", entities: foundEntities };
}
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _config?: GlanceCardConfig;
@state() private _entityIds?: string[];
@state()
@consumeEntityStates({ entityIdPath: ["_entityIds"] })
private _stateObjs?: Record<string, HassEntity>;
@state()
@consume({ context: entitiesContext, subscribe: true })
@transform<
HomeAssistant["entities"],
Record<string, EntityRegistryDisplayEntry>
>({
transformer: function (this: HuiGlanceCard, entities) {
return this._selectEntityEntries(entities);
},
watch: ["_entityIds"],
})
private _entityEntries?: Record<string, EntityRegistryDisplayEntry>;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
@state()
@consume({ context: configContext, subscribe: true })
@transform<HomeAssistantConfig, HassConfig>({
transformer: ({ config }) => config,
})
private _hassConfig!: HassConfig;
@state()
@consume({ context: formattersContext, subscribe: true })
private _formatters!: HomeAssistantFormatters;
@state()
@consume({ context: uiContext, subscribe: true })
@transform<HomeAssistantUI, Themes>({
transformer: ({ themes }) => themes,
})
private _themes!: Themes;
private _configEntities?: GlanceConfigEntity[];
public getCardSize(): number {
@@ -123,18 +182,11 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
this.style.setProperty("--glance-column-width", `${100 / columns}%`);
this._configEntities = entities;
if (this.hass) {
this.requestUpdate();
}
}
protected shouldUpdate(changedProps: PropertyValues<this>): boolean {
return hasConfigOrEntitiesChanged(this, changedProps);
this._entityIds = entities.map((entityConf) => entityConf.entity);
}
protected render() {
if (!this._config || !this.hass) {
if (!this._config) {
return nothing;
}
const { title } = this._config;
@@ -152,21 +204,23 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
protected updated(changedProps: PropertyValues): void {
super.updated(changedProps);
if (!this._config || !this.hass) {
if (!this._config || !this._themes) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!changedProps.has("_themes") && !changedProps.has("_config")) {
return;
}
const oldThemes = changedProps.get("_themes") as Themes | undefined;
const oldConfig = changedProps.get("_config") as
GlanceCardConfig | undefined;
if (
!oldHass ||
!oldConfig ||
oldHass.themes !== this.hass.themes ||
oldConfig.theme !== this._config.theme
(changedProps.has("_themes") &&
(!oldThemes || oldThemes !== this._themes)) ||
(changedProps.has("_config") &&
(!oldConfig || oldConfig.theme !== this._config.theme))
) {
applyThemesOnElement(this, this.hass.themes, this._config.theme);
applyThemesOnElement(this, this._themes, this._config.theme);
}
}
@@ -242,8 +296,36 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
}
`;
/** Reuses the previous record while every selected entry is unchanged. */
private _selectEntityEntries(
entities: HomeAssistant["entities"] | undefined
): Record<string, EntityRegistryDisplayEntry> | undefined {
if (!this._entityIds || !entities) {
return undefined;
}
const next: Record<string, EntityRegistryDisplayEntry> = {};
for (const id of this._entityIds) {
const entry = entities[id];
if (entry !== undefined) {
next[id] = entry;
}
}
const previous = this._entityEntries;
if (!previous) {
return next;
}
const nextKeys = Object.keys(next);
if (
Object.keys(previous).length === nextKeys.length &&
nextKeys.every((key) => previous[key] === next[key])
) {
return previous;
}
return next;
}
private _renderEntity(entityConf: GlanceConfigEntity): TemplateResult {
const stateObj = this.hass!.states[entityConf.entity];
const stateObj = this._stateObjs?.[entityConf.entity];
if (!stateObj) {
return html`<div class="entity warning">
@@ -251,7 +333,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
this._config!.show_name
? html`
<div class="name">
${createEntityNotFoundWarning(this.hass!.localize, this.hass!.config)}
${createEntityNotFoundWarning(this._localize, this._hassConfig)}
</div>
`
: ""
@@ -259,7 +341,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
${
this._config!.show_icon
? html` <hui-warning-element
.label=${createEntityNotFoundWarning(this.hass!.localize, this.hass!.config)}
.label=${createEntityNotFoundWarning(this._localize, this._hassConfig)}
></hui-warning-element>`
: ""
}
@@ -267,7 +349,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
</div>`;
}
const name = this.hass!.formatEntityName(stateObj, entityConf.name);
const name = this._formatters.formatEntityName(stateObj, entityConf.name);
const domain = computeDomain(entityConf.entity);
return html`
@@ -334,7 +416,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
capitalize
></ha-relative-time>
`
: this.hass!.formatEntityState(stateObj)
: this._formatters.formatEntityState(stateObj)
}
</div>
`
@@ -351,7 +433,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
ActionHandlerEvent
) {
const { config } = ev.currentTarget;
handleAction(this, this.hass!, config, ev.detail.action!);
fireEvent(this, "hass-action", { config, action: ev.detail.action });
}
}
+8 -5
View File
@@ -4,10 +4,11 @@ import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import { consumeLocalize } from "../../../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../../../common/translations/localize";
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
import "../../../components/ha-alert";
import "../../../components/ha-card";
import type { HomeAssistant } from "../../../types";
import type {
LovelaceCard,
LovelaceCardEditor,
@@ -34,10 +35,12 @@ export class HuiIframeCard extends LitElement implements LovelaceCard {
@property({ attribute: false })
public layout?: string;
@property({ attribute: false }) public hass?: HomeAssistant;
@state() protected _config?: IframeCardConfig;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
public getCardSize(): number {
if (!this._config) {
return 5;
@@ -63,7 +66,7 @@ export class HuiIframeCard extends LitElement implements LovelaceCard {
}
protected render() {
if (!this._config || !this.hass) {
if (!this._config) {
return nothing;
}
@@ -85,7 +88,7 @@ export class HuiIframeCard extends LitElement implements LovelaceCard {
if (location.protocol === "https:" && target_protocol !== "https:") {
return html`
<ha-alert alert-type="error">
${this.hass!.localize(
${this._localize(
"ui.panel.lovelace.cards.iframe.error_secure_context",
{
target_protocol,
+23 -12
View File
@@ -1,18 +1,33 @@
import type { HassConfig } from "home-assistant-js-websocket";
import { STATE_NOT_RUNNING } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { css, html, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { consume } from "../../../common/decorators/consume";
import { consumeLocalize } from "../../../common/decorators/consume-context-entry";
import { transform } from "../../../common/decorators/transform";
import { fireEvent } from "../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/animation/ha-fade-in";
import "../../../components/ha-card";
import "../../../components/ha-spinner";
import { configContext } from "../../../data/context";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import type { HomeAssistant } from "../../../types";
import type { HomeAssistantConfig } from "../../../types";
import type { LovelaceCard } from "../types";
@customElement("hui-starting-card")
export class HuiStartingCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass?: HomeAssistant;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
@state()
@consume({ context: configContext, subscribe: true })
@transform<HomeAssistantConfig, HassConfig>({
transformer: ({ config }) => config,
})
private _hassConfig?: HassConfig;
public getCardSize(): number {
return 2;
@@ -21,28 +36,24 @@ export class HuiStartingCard extends LitElement implements LovelaceCard {
// eslint-disable-next-line @typescript-eslint/no-empty-function
public setConfig(_config: LovelaceCardConfig): void {}
protected updated(changedProperties: PropertyValues<this>) {
protected updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
if (!changedProperties.has("hass") || !this.hass!.config) {
if (!changedProperties.has("_hassConfig") || !this._hassConfig) {
return;
}
if (this.hass!.config.state !== STATE_NOT_RUNNING) {
if (this._hassConfig.state !== STATE_NOT_RUNNING) {
fireEvent(this, "config-refresh");
}
}
protected render() {
if (!this.hass) {
return nothing;
}
return html`
<div class="content">
<ha-fade-in .delay=${500}>
<ha-spinner></ha-spinner>
</ha-fade-in>
${this.hass.localize("ui.panel.lovelace.cards.starting.description")}
${this._localize("ui.panel.lovelace.cards.starting.description")}
</div>
`;
}
@@ -1,8 +1,14 @@
import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import { computeCssColor } from "../../../common/color/compute-color";
import { consume } from "../../../common/decorators/consume";
import {
consumeEntityStates,
consumeLocalize,
} from "../../../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../../../common/translations/localize";
import { computeDomain } from "../../../common/entity/compute_domain";
import { stateActive } from "../../../common/entity/state_active";
import { stateColorCss } from "../../../common/entity/state_color";
@@ -10,18 +16,28 @@ import "../../../components/ha-card";
import "../../../components/tile/ha-tile-container";
import "../../../components/tile/ha-tile-icon";
import "../../../components/tile/ha-tile-info";
import { apiContext } from "../../../data/context";
import { forwardHaptic } from "../../../data/haptics";
import type { HomeAssistant } from "../../../types";
import type { HomeAssistantApi } from "../../../types";
import type { LovelaceCard, LovelaceGridOptions } from "../types";
import type { ToggleGroupCardConfig } from "./types";
import { getToggleAction } from "../../../common/entity/get_toggle_action";
@customElement("hui-toggle-group-card")
export class HuiToggleGroupCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _config?: ToggleGroupCardConfig;
@state()
@consumeEntityStates({ entityIdPath: ["_config", "entities"] })
private _stateObjs?: Record<string, HassEntity>;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
@consume({ context: apiContext, subscribe: true })
private _api!: HomeAssistantApi;
public setConfig(config: ToggleGroupCardConfig): void {
if (!config.entities?.length) {
throw new Error("Entities are required");
@@ -52,17 +68,14 @@ export class HuiToggleGroupCard extends LitElement implements LovelaceCard {
}
private _getOnEntities(): HassEntity[] {
if (!this.hass || !this._config) return [];
return this._config.entities
.map((entityId) => this.hass!.states[entityId])
.filter(
(stateObj): stateObj is HassEntity =>
stateObj !== undefined && stateActive(stateObj)
);
if (!this._stateObjs) return [];
return Object.values(this._stateObjs).filter((stateObj) =>
stateActive(stateObj)
);
}
private _computeColor(): string | undefined {
if (!this._config || !this.hass) return undefined;
if (!this._config) return undefined;
const onEntities = this._getOnEntities();
if (onEntities.length === 0) return undefined;
@@ -75,17 +88,17 @@ export class HuiToggleGroupCard extends LitElement implements LovelaceCard {
}
private _computeLabel(): string {
if (!this.hass || !this._config) return "";
if (!this._config) return "";
const onCount = this._getOnEntities().length;
const total = this._config.entities.length;
if (onCount === 0) {
return this.hass.localize("ui.card.toggle-group.all_off");
return this._localize("ui.card.toggle-group.all_off");
}
if (onCount === total) {
return this.hass.localize("ui.card.toggle-group.all_on");
return this._localize("ui.card.toggle-group.all_on");
}
return this.hass.localize("ui.card.toggle-group.some_on", {
return this._localize("ui.card.toggle-group.some_on", {
on_count: onCount,
total_count: total,
});
@@ -95,14 +108,14 @@ export class HuiToggleGroupCard extends LitElement implements LovelaceCard {
const turnOn = this._getOnEntities().length === 0;
const title = this._config!.title;
if (title) {
return this.hass!.localize(
return this._localize(
turnOn
? "ui.card.toggle-group.turn_on_name"
: "ui.card.toggle-group.turn_off_name",
{ name: title }
);
}
return this.hass!.localize(
return this._localize(
turnOn
? "ui.card.toggle-group.turn_all_on"
: "ui.card.toggle-group.turn_all_off"
@@ -110,20 +123,20 @@ export class HuiToggleGroupCard extends LitElement implements LovelaceCard {
}
private _handleTap(): void {
if (!this.hass || !this._config) return;
if (!this._config) return;
const onEntities = this._getOnEntities();
const domain = computeDomain(this._config.entities[0]);
const service = getToggleAction(domain, onEntities.length === 0);
this.hass.callService(domain, service, {
this._api.callService(domain, service, {
entity_id: this._config.entities,
});
forwardHaptic(this, "light");
}
protected render() {
if (!this._config || !this.hass) {
if (!this._config) {
return nothing;
}