diff --git a/src/common/util/promise-timeout.ts b/src/common/util/promise-timeout.ts index b26e8ba4b8..43b3359026 100644 --- a/src/common/util/promise-timeout.ts +++ b/src/common/util/promise-timeout.ts @@ -1,4 +1,4 @@ -export const promiseTimeout = (ms: number, promise: Promise) => { +export const promiseTimeout = (ms: number, promise: Promise | any) => { const timeout = new Promise((_resolve, reject) => { setTimeout(() => { reject(`Timed out in ${ms} ms.`); diff --git a/src/panels/lovelace/common/compute-card-size.ts b/src/panels/lovelace/common/compute-card-size.ts index 60ca74c4a6..ee22dcbe2d 100644 --- a/src/panels/lovelace/common/compute-card-size.ts +++ b/src/panels/lovelace/common/compute-card-size.ts @@ -1,10 +1,17 @@ +import { promiseTimeout } from "../../../common/util/promise-timeout"; import { LovelaceCard, LovelaceHeaderFooter } from "../types"; export const computeCardSize = ( card: LovelaceCard | LovelaceHeaderFooter ): number | Promise => { if (typeof card.getCardSize === "function") { - return card.getCardSize(); + try { + return promiseTimeout(500, card.getCardSize()).catch( + () => 1 + ) as Promise; + } catch (_e: any) { + return 1; + } } if (customElements.get(card.localName)) { return 1; diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts index 685adce5c9..bcf440f328 100644 --- a/src/panels/lovelace/views/hui-view.ts +++ b/src/panels/lovelace/views/hui-view.ts @@ -10,10 +10,18 @@ import type { LovelaceViewElement, } from "../../../data/lovelace"; import type { HomeAssistant } from "../../../types"; +import { + createErrorBadgeConfig, + createErrorBadgeElement, +} from "../badges/hui-error-badge"; import type { HuiErrorCard } from "../cards/hui-error-card"; import { processConfigEntities } from "../common/process-config-entities"; import { createBadgeElement } from "../create-element/create-badge-element"; import { createCardElement } from "../create-element/create-card-element"; +import { + createErrorCardConfig, + createErrorCardElement, +} from "../create-element/create-element-base"; import { createViewElement } from "../create-element/create-view-element"; import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog"; import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; @@ -54,7 +62,13 @@ export class HUIView extends ReactiveElement { // Public to make demo happy public createCardElement(cardConfig: LovelaceCardConfig) { const element = createCardElement(cardConfig) as LovelaceCard; - element.hass = this.hass; + try { + element.hass = this.hass; + } catch (e: any) { + return createErrorCardElement( + createErrorCardConfig(e.message, cardConfig) + ); + } element.addEventListener( "ll-rebuild", (ev: Event) => { @@ -71,7 +85,11 @@ export class HUIView extends ReactiveElement { public createBadgeElement(badgeConfig: LovelaceBadgeConfig) { const element = createBadgeElement(badgeConfig) as LovelaceBadge; - element.hass = this.hass; + try { + element.hass = this.hass; + } catch (e: any) { + return createErrorBadgeElement(createErrorBadgeConfig(e.message)); + } element.addEventListener( "ll-badge-rebuild", () => { @@ -121,11 +139,19 @@ export class HUIView extends ReactiveElement { // Config has not changed. Just props if (changedProperties.has("hass")) { this._badges.forEach((badge) => { - badge.hass = this.hass; + try { + badge.hass = this.hass; + } catch (e: any) { + this._rebuildBadge(badge, createErrorBadgeConfig(e.message)); + } }); this._cards.forEach((element) => { - element.hass = this.hass; + try { + element.hass = this.hass; + } catch (e: any) { + this._rebuildCard(element, createErrorCardConfig(e.message, null)); + } }); this._layoutElement.hass = this.hass; @@ -238,7 +264,11 @@ export class HUIView extends ReactiveElement { const badges = processConfigEntities(config.badges as any); this._badges = badges.map((badge) => { const element = createBadgeElement(badge); - element.hass = this.hass; + try { + element.hass = this.hass; + } catch (e: any) { + return createErrorBadgeElement(createErrorBadgeConfig(e.message)); + } return element; }); } @@ -251,7 +281,13 @@ export class HUIView extends ReactiveElement { this._cards = config.cards.map((cardConfig) => { const element = this.createCardElement(cardConfig); - element.hass = this.hass; + try { + element.hass = this.hass; + } catch (e: any) { + return createErrorCardElement( + createErrorCardConfig(e.message, cardConfig) + ); + } return element; }); } @@ -260,8 +296,14 @@ export class HUIView extends ReactiveElement { cardElToReplace: LovelaceCard, config: LovelaceCardConfig ): void { - const newCardEl = this.createCardElement(config); - newCardEl.hass = this.hass; + let newCardEl = this.createCardElement(config); + try { + newCardEl.hass = this.hass; + } catch (e: any) { + newCardEl = createErrorCardElement( + createErrorCardConfig(e.message, config) + ); + } if (cardElToReplace.parentElement) { cardElToReplace.parentElement!.replaceChild(newCardEl, cardElToReplace); } @@ -274,8 +316,12 @@ export class HUIView extends ReactiveElement { badgeElToReplace: LovelaceBadge, config: LovelaceBadgeConfig ): void { - const newBadgeEl = this.createBadgeElement(config); - newBadgeEl.hass = this.hass; + let newBadgeEl = this.createBadgeElement(config); + try { + newBadgeEl.hass = this.hass; + } catch (e: any) { + newBadgeEl = createErrorBadgeElement(createErrorBadgeConfig(e.message)); + } if (badgeElToReplace.parentElement) { badgeElToReplace.parentElement!.replaceChild( newBadgeEl,