From 6741fdb34f5ec7248e39ec3241d3538378441e60 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 28 Feb 2024 10:27:26 +0100 Subject: [PATCH 01/25] Add grid support to climate card --- src/cards/climate-card/climate-card.ts | 79 +++++++++++++++++--------- src/shared/card.ts | 3 +- src/utils/card-styles.ts | 6 ++ src/utils/theme.ts | 2 +- 4 files changed, 62 insertions(+), 28 deletions(-) diff --git a/src/cards/climate-card/climate-card.ts b/src/cards/climate-card/climate-card.ts index 72b2f4c7f..71ef5ae85 100644 --- a/src/cards/climate-card/climate-card.ts +++ b/src/cards/climate-card/climate-card.ts @@ -70,7 +70,24 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { @state() private _activeControl?: ClimateCardControl; - @state() private _controls: ClimateCardControl[] = []; + @state() private _inGrid = false; + + private get _controls(): ClimateCardControl[] { + if (!this._config || !this.hass || !this._config.entity) return []; + + const entityId = this._config.entity; + const stateObj = this.hass.states[entityId] as ClimateEntity | undefined; + if (!stateObj) return []; + + const controls: ClimateCardControl[] = []; + if (isTemperatureControlVisible(stateObj) && this._config.show_temperature_control) { + controls.push("temperature_control"); + } + if (isHvacModesVisible(stateObj, this._config.hvac_modes)) { + controls.push("hvac_mode_control"); + } + return controls; + } _onControlTap(ctrl, e): void { e.stopPropagation(); @@ -91,39 +108,21 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { }, ...config, }; - this.updateControls(); + this.updateActiveControl(); } protected updated(changedProperties: PropertyValues) { super.updated(changedProperties); if (this.hass && changedProperties.has("hass")) { - this.updateControls(); + this.updateActiveControl(); } } - updateControls() { - if (!this._config || !this.hass || !this._config.entity) return; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as ClimateEntity | undefined; - - if (!stateObj) return; - - const controls: ClimateCardControl[] = []; - if (!this._config.collapsible_controls || isActive(stateObj)) { - if (isTemperatureControlVisible(stateObj) && this._config.show_temperature_control) { - controls.push("temperature_control"); - } - if (isHvacModesVisible(stateObj, this._config.hvac_modes)) { - controls.push("hvac_mode_control"); - } - } - - this._controls = controls; + updateActiveControl() { const isActiveControlSupported = this._activeControl - ? controls.includes(this._activeControl) + ? this._controls.includes(this._activeControl) : false; - this._activeControl = isActiveControlSupported ? this._activeControl : controls[0]; + this._activeControl = isActiveControlSupported ? this._activeControl : this._controls[0]; } private _handleAction(ev: ActionHandlerEvent) { @@ -166,8 +165,16 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { } const rtl = computeRTL(this.hass); + const isControlVisible = + (!this._config.collapsible_controls || isActive(stateObj)) && this._controls.length; + return html` - + - ${this._controls.length > 0 + ${isControlVisible ? html`
${this.renderActiveControl(stateObj)}${this.renderOtherControls()} @@ -283,6 +290,26 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { } } + public getGridSize(): [number, number] { + this._inGrid = true; + let column = 2; + let row = 1; + if (!this._config) return [column, row]; + + const appearance = computeAppearance(this._config); + if (appearance.layout === "vertical") { + row += 1; + } + if (this._controls.length) { + if (appearance.layout === "horizontal") { + column = 4; + } else if (!this._config?.collapsible_controls) { + row += 1; + } + } + return [column, row]; + } + static get styles(): CSSResultGroup { return [ super.styles, diff --git a/src/shared/card.ts b/src/shared/card.ts index d8adb442b..cca62383b 100644 --- a/src/shared/card.ts +++ b/src/shared/card.ts @@ -32,7 +32,8 @@ export class Card extends LitElement { flex-shrink: 0; flex-grow: 0; box-sizing: border-box; - justify-content: center; + justify-content: space-between; + height: 100%; } .container > ::slotted(*:not(:last-child)) { margin-bottom: var(--spacing); diff --git a/src/utils/card-styles.ts b/src/utils/card-styles.ts index 1365a2312..6bd471b5a 100644 --- a/src/utils/card-styles.ts +++ b/src/utils/card-styles.ts @@ -12,6 +12,12 @@ export const cardStyle = css` ha-card.fill-container { height: 100%; } + ha-card.in-grid { + height: 100%; + } + ha-card.in-grid mushroom-card { + height: 100%; + } .actions { display: flex; flex-direction: row; diff --git a/src/utils/theme.ts b/src/utils/theme.ts index 3a3fc02d5..16071a8fb 100644 --- a/src/utils/theme.ts +++ b/src/utils/theme.ts @@ -51,7 +51,7 @@ export const themeVariables = css` ); /* Controls */ --control-border-radius: var(--mush-control-border-radius, 12px); - --control-height: var(--mush-control-height, 42px); + --control-height: var(--mush-control-height, 40px); --control-button-ratio: var(--mush-control-button-ratio, 1); --control-icon-size: var(--mush-control-icon-size, 0.5em); From 6343fab9064dc1ca3649eb207bee0fa7275d084a Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 12:05:49 +0100 Subject: [PATCH 02/25] Add grid support to light card --- src/cards/climate-card/climate-card.ts | 51 +++++++------- src/cards/light-card/light-card.ts | 97 ++++++++++++++++---------- 2 files changed, 88 insertions(+), 60 deletions(-) diff --git a/src/cards/climate-card/climate-card.ts b/src/cards/climate-card/climate-card.ts index 71ef5ae85..1ef847e7a 100644 --- a/src/cards/climate-card/climate-card.ts +++ b/src/cards/climate-card/climate-card.ts @@ -89,15 +89,35 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { return controls; } + public getCardSize(): number | Promise { + return 1; + } + + public getGridSize(): [number, number] { + this._inGrid = true; + let column = 2; + let row = 1; + if (!this._config) return [column, row]; + + const appearance = computeAppearance(this._config); + if (appearance.layout === "vertical") { + row += 1; + } + if (this._controls.length) { + if (appearance.layout === "horizontal") { + column = 4; + } else if (!this._config?.collapsible_controls) { + row += 1; + } + } + return [column, row]; + } + _onControlTap(ctrl, e): void { e.stopPropagation(); this._activeControl = ctrl; } - getCardSize(): number | Promise { - return 1; - } - setConfig(config: ClimateCardConfig): void { this._config = { tap_action: { @@ -192,7 +212,8 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { ${isControlVisible ? html`
- ${this.renderActiveControl(stateObj)}${this.renderOtherControls()} + ${this.renderActiveControl(stateObj)} + ${this.renderOtherControls()}
` : nothing} @@ -290,26 +311,6 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { } } - public getGridSize(): [number, number] { - this._inGrid = true; - let column = 2; - let row = 1; - if (!this._config) return [column, row]; - - const appearance = computeAppearance(this._config); - if (appearance.layout === "vertical") { - row += 1; - } - if (this._controls.length) { - if (appearance.layout === "horizontal") { - column = 4; - } else if (!this._config?.collapsible_controls) { - row += 1; - } - } - return [column, row]; - } - static get styles(): CSSResultGroup { return [ super.styles, diff --git a/src/cards/light-card/light-card.ts b/src/cards/light-card/light-card.ts index a2153d801..5692ade25 100644 --- a/src/cards/light-card/light-card.ts +++ b/src/cards/light-card/light-card.ts @@ -78,17 +78,54 @@ export class LightCard extends MushroomBaseCard implements LovelaceCard { @state() private _activeControl?: LightCardControl; - @state() private _controls: LightCardControl[] = []; + @state() private brightness?: number; - _onControlTap(ctrl, e): void { - e.stopPropagation(); - this._activeControl = ctrl; + @state() private _inGrid = false; + + private get _controls(): LightCardControl[] { + if (!this._config || !this.hass || !this._config.entity) return []; + + const entityId = this._config.entity; + const stateObj = this.hass.states[entityId] as LightEntity | undefined; + if (!stateObj) return []; + + const controls: LightCardControl[] = []; + if (this._config.show_brightness_control && supportsBrightnessControl(stateObj)) { + controls.push("brightness_control"); + } + if (this._config.show_color_temp_control && supportsColorTempControl(stateObj)) { + controls.push("color_temp_control"); + } + if (this._config.show_color_control && supportsColorControl(stateObj)) { + controls.push("color_control"); + } + return controls; } - getCardSize(): number | Promise { + public getCardSize(): number | Promise { return 1; } + public getGridSize(): [number, number] { + this._inGrid = true; + let column = 2; + let row = 1; + if (!this._config) return [column, row]; + + const appearance = computeAppearance(this._config); + if (appearance.layout === "vertical") { + row += 1; + } + if (this._controls.length) { + if (appearance.layout === "horizontal") { + column = 4; + } else if (!this._config?.collapsible_controls) { + row += 1; + } + } + return [column, row]; + } + setConfig(config: LightCardConfig): void { this._config = { tap_action: { @@ -99,21 +136,23 @@ export class LightCard extends MushroomBaseCard implements LovelaceCard { }, ...config, }; - this.updateControls(); + this.updateActiveControls(); this.updateBrightness(); } + _onControlTap(ctrl, e): void { + e.stopPropagation(); + this._activeControl = ctrl; + } + protected updated(changedProperties: PropertyValues) { super.updated(changedProperties); if (this.hass && changedProperties.has("hass")) { - this.updateControls(); + this.updateActiveControls(); this.updateBrightness(); } } - @state() - private brightness?: number; - updateBrightness() { this.brightness = undefined; if (!this._config || !this.hass || !this._config.entity) return; @@ -131,31 +170,11 @@ export class LightCard extends MushroomBaseCard implements LovelaceCard { } } - updateControls() { - if (!this._config || !this.hass || !this._config.entity) return; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as LightEntity | undefined; - - if (!stateObj) return; - - const controls: LightCardControl[] = []; - if (!this._config.collapsible_controls || isActive(stateObj)) { - if (this._config.show_brightness_control && supportsBrightnessControl(stateObj)) { - controls.push("brightness_control"); - } - if (this._config.show_color_temp_control && supportsColorTempControl(stateObj)) { - controls.push("color_temp_control"); - } - if (this._config.show_color_control && supportsColorControl(stateObj)) { - controls.push("color_control"); - } - } - this._controls = controls; + updateActiveControls() { const isActiveControlSupported = this._activeControl - ? controls.includes(this._activeControl) + ? this._controls.includes(this._activeControl) : false; - this._activeControl = isActiveControlSupported ? this._activeControl : controls[0]; + this._activeControl = isActiveControlSupported ? this._activeControl : this._controls[0]; } private _handleAction(ev: ActionHandlerEvent) { @@ -194,8 +213,16 @@ export class LightCard extends MushroomBaseCard implements LovelaceCard { const rtl = computeRTL(this.hass); + const isControlVisible = + (!this._config.collapsible_controls || isActive(stateObj)) && this._controls.length; + return html` - + - ${this._controls.length > 0 + ${isControlVisible ? html`
${this.renderActiveControl(stateObj)} From 6fe0c7f0aba826162ba586931bd54d62ae28cfbf Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 12:12:40 +0100 Subject: [PATCH 03/25] Add grid support to media_player card --- .../media-player-card/media-player-card.ts | 81 ++++++++++++------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/src/cards/media-player-card/media-player-card.ts b/src/cards/media-player-card/media-player-card.ts index 3526058e9..3bbb3bd71 100644 --- a/src/cards/media-player-card/media-player-card.ts +++ b/src/cards/media-player-card/media-player-card.ts @@ -76,17 +76,54 @@ export class MediaPlayerCard extends MushroomBaseCard implements LovelaceCard { @state() private _activeControl?: MediaPlayerCardControl; - @state() private _controls: MediaPlayerCardControl[] = []; + @state() private _inGrid = false; - _onControlTap(ctrl, e): void { - e.stopPropagation(); - this._activeControl = ctrl; + private get _controls(): MediaPlayerCardControl[] { + if (!this._config || !this.hass || !this._config.entity) return []; + + const entityId = this._config.entity; + const stateObj = this.hass.states[entityId] as MediaPlayerEntity | undefined; + if (!stateObj) return []; + + const controls: MediaPlayerCardControl[] = []; + if (isMediaControlVisible(stateObj, this._config.media_controls)) { + controls.push("media_control"); + } + if (isVolumeControlVisible(stateObj, this._config.volume_controls)) { + controls.push("volume_control"); + } + return controls; } - getCardSize(): number | Promise { + public getCardSize(): number | Promise { return 1; } + public getGridSize(): [number, number] { + this._inGrid = true; + let column = 2; + let row = 1; + if (!this._config) return [column, row]; + + const appearance = computeAppearance(this._config); + if (appearance.layout === "vertical") { + row += 1; + } + if (this._controls.length) { + if (appearance.layout === "horizontal") { + column = 4; + } else if (!this._config?.collapsible_controls) { + row += 1; + } + } + return [column, row]; + } + + _onControlTap(ctrl, e): void { + e.stopPropagation(); + this._activeControl = ctrl; + } + setConfig(config: MediaPlayerCardConfig): void { this._config = { tap_action: { @@ -131,28 +168,10 @@ export class MediaPlayerCard extends MushroomBaseCard implements LovelaceCard { } updateControls() { - if (!this._config || !this.hass || !this._config.entity) return; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as MediaPlayerEntity | undefined; - - if (!stateObj) return; - - const controls: MediaPlayerCardControl[] = []; - if (!this._config.collapsible_controls || isActive(stateObj)) { - if (isMediaControlVisible(stateObj, this._config?.media_controls)) { - controls.push("media_control"); - } - if (isVolumeControlVisible(stateObj, this._config.volume_controls)) { - controls.push("volume_control"); - } - } - - this._controls = controls; const isActiveControlSupported = this._activeControl - ? controls.includes(this._activeControl) + ? this._controls.includes(this._activeControl) : false; - this._activeControl = isActiveControlSupported ? this._activeControl : controls[0]; + this._activeControl = isActiveControlSupported ? this._activeControl : this._controls[0]; } private _handleAction(ev: ActionHandlerEvent) { @@ -184,8 +203,16 @@ export class MediaPlayerCard extends MushroomBaseCard implements LovelaceCard { const rtl = computeRTL(this.hass); + const isControlVisible = + (!this._config.collapsible_controls || isActive(stateObj)) && this._controls.length; + return html` - + - ${this._controls.length > 0 + ${isControlVisible ? html`
${this.renderActiveControl(stateObj, appearance.layout)} From 9e1210647fc3e2a3cdd3cf74b8aba4d587fb5fd4 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 12:20:47 +0100 Subject: [PATCH 04/25] Add grid support to template card --- src/cards/climate-card/climate-card.ts | 15 ++++++----- src/cards/light-card/light-card.ts | 15 ++++++----- .../media-player-card/media-player-card.ts | 15 ++++++----- src/cards/template-card/template-card.ts | 27 +++++++++++++++++-- 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/cards/climate-card/climate-card.ts b/src/cards/climate-card/climate-card.ts index 1ef847e7a..135d9daea 100644 --- a/src/cards/climate-card/climate-card.ts +++ b/src/cards/climate-card/climate-card.ts @@ -103,12 +103,15 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { if (appearance.layout === "vertical") { row += 1; } - if (this._controls.length) { - if (appearance.layout === "horizontal") { - column = 4; - } else if (!this._config?.collapsible_controls) { - row += 1; - } + if (appearance.layout === "horizontal") { + column = 4; + } + if ( + this._controls.length && + !this._config?.collapsible_controls && + appearance.layout !== "horizontal" + ) { + row += 1; } return [column, row]; } diff --git a/src/cards/light-card/light-card.ts b/src/cards/light-card/light-card.ts index 5692ade25..ba35686e1 100644 --- a/src/cards/light-card/light-card.ts +++ b/src/cards/light-card/light-card.ts @@ -116,12 +116,15 @@ export class LightCard extends MushroomBaseCard implements LovelaceCard { if (appearance.layout === "vertical") { row += 1; } - if (this._controls.length) { - if (appearance.layout === "horizontal") { - column = 4; - } else if (!this._config?.collapsible_controls) { - row += 1; - } + if (appearance.layout === "horizontal") { + column = 4; + } + if ( + this._controls.length && + !this._config?.collapsible_controls && + appearance.layout !== "horizontal" + ) { + row += 1; } return [column, row]; } diff --git a/src/cards/media-player-card/media-player-card.ts b/src/cards/media-player-card/media-player-card.ts index 3bbb3bd71..3fa384277 100644 --- a/src/cards/media-player-card/media-player-card.ts +++ b/src/cards/media-player-card/media-player-card.ts @@ -109,12 +109,15 @@ export class MediaPlayerCard extends MushroomBaseCard implements LovelaceCard { if (appearance.layout === "vertical") { row += 1; } - if (this._controls.length) { - if (appearance.layout === "horizontal") { - column = 4; - } else if (!this._config?.collapsible_controls) { - row += 1; - } + if (appearance.layout === "horizontal") { + column = 4; + } + if ( + this._controls.length && + !this._config?.collapsible_controls && + appearance.layout !== "horizontal" + ) { + row += 1; } return [column, row]; } diff --git a/src/cards/template-card/template-card.ts b/src/cards/template-card/template-card.ts index 86233c856..5b4d15584 100644 --- a/src/cards/template-card/template-card.ts +++ b/src/cards/template-card/template-card.ts @@ -69,10 +69,28 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard { @state() private _unsubRenderTemplates: Map> = new Map(); - getCardSize(): number | Promise { + @state() private _inGrid = false; + + public getCardSize(): number | Promise { return 1; } + public getGridSize(): [number, number] { + this._inGrid = true; + let column = 2; + let row = 1; + if (!this._config) return [column, row]; + + const appearance = computeAppearance(this._config); + if (appearance.layout === "vertical") { + row += 1; + } + if (appearance.layout === "horizontal") { + column = 4; + } + return [column, row]; + } + setConfig(config: TemplateCardConfig): void { TEMPLATE_KEYS.forEach((key) => { if (this._config?.[key] !== config[key] || this._config?.entity != config.entity) { @@ -142,7 +160,12 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard { const weatherSvg = getWeatherSvgIcon(icon); return html` - + Date: Tue, 5 Mar 2024 12:41:04 +0100 Subject: [PATCH 05/25] Add logic in base card --- src/cards/entity-card/entity-card.ts | 12 +++-------- src/cards/light-card/light-card.ts | 31 ++++++--------------------- src/utils/base-card.ts | 32 +++++++++++++++++++++++++++- src/utils/card-styles.ts | 4 ++-- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/cards/entity-card/entity-card.ts b/src/cards/entity-card/entity-card.ts index 81dd1b609..4da0ac530 100644 --- a/src/cards/entity-card/entity-card.ts +++ b/src/cards/entity-card/entity-card.ts @@ -36,7 +36,7 @@ registerCustomCard({ }); @customElement(ENTITY_CARD_NAME) -export class EntityCard extends MushroomBaseCard implements LovelaceCard { +export class EntityCard extends MushroomBaseCard implements LovelaceCard { public static async getConfigElement(): Promise { await import("./entity-card-editor"); return document.createElement(ENTITY_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -50,14 +50,8 @@ export class EntityCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: EntityCardConfig; - - getCardSize(): number | Promise { - return 1; - } - setConfig(config: EntityCardConfig): void { - this._config = { + super.setConfig({ tap_action: { action: "more-info", }, @@ -65,7 +59,7 @@ export class EntityCard extends MushroomBaseCard implements LovelaceCard { action: "more-info", }, ...config, - }; + }); } private _handleAction(ev: ActionHandlerEvent) { diff --git a/src/cards/light-card/light-card.ts b/src/cards/light-card/light-card.ts index ba35686e1..7a8e4a0fc 100644 --- a/src/cards/light-card/light-card.ts +++ b/src/cards/light-card/light-card.ts @@ -59,7 +59,7 @@ registerCustomCard({ }); @customElement(LIGHT_CARD_NAME) -export class LightCard extends MushroomBaseCard implements LovelaceCard { +export class LightCard extends MushroomBaseCard implements LovelaceCard { public static async getConfigElement(): Promise { await import("./light-card-editor"); return document.createElement(LIGHT_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -74,14 +74,10 @@ export class LightCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: LightCardConfig; - @state() private _activeControl?: LightCardControl; @state() private brightness?: number; - @state() private _inGrid = false; - private get _controls(): LightCardControl[] { if (!this._config || !this.hass || !this._config.entity) return []; @@ -102,35 +98,22 @@ export class LightCard extends MushroomBaseCard implements LovelaceCard { return controls; } - public getCardSize(): number | Promise { - return 1; - } - public getGridSize(): [number, number] { - this._inGrid = true; - let column = 2; - let row = 1; - if (!this._config) return [column, row]; - + const size = super.getGridSize(); + if (!this._config) return size; const appearance = computeAppearance(this._config); - if (appearance.layout === "vertical") { - row += 1; - } - if (appearance.layout === "horizontal") { - column = 4; - } if ( this._controls.length && !this._config?.collapsible_controls && appearance.layout !== "horizontal" ) { - row += 1; + size[1] += 1; } - return [column, row]; + return size; } setConfig(config: LightCardConfig): void { - this._config = { + super.setConfig({ tap_action: { action: "toggle", }, @@ -138,7 +121,7 @@ export class LightCard extends MushroomBaseCard implements LovelaceCard { action: "more-info", }, ...config, - }; + }); this.updateActiveControls(); this.updateBrightness(); } diff --git a/src/utils/base-card.ts b/src/utils/base-card.ts index 02ffd7bbe..4db029a3b 100644 --- a/src/utils/base-card.ts +++ b/src/utils/base-card.ts @@ -1,5 +1,6 @@ import { HassEntity } from "home-assistant-js-websocket"; import { html, nothing, TemplateResult } from "lit"; +import { property, state } from "lit/decorators.js"; import { classMap } from "lit/directives/class-map.js"; import { computeRTL, computeStateDisplay, HomeAssistant, isActive, isAvailable } from "../ha"; import setupCustomlocalize from "../localize"; @@ -21,7 +22,36 @@ export function computeDarkMode(hass?: HomeAssistant): boolean { if (!hass) return false; return (hass.themes as any).darkMode as boolean; } -export class MushroomBaseCard extends MushroomBaseElement { +export class MushroomBaseCard extends MushroomBaseElement { + @state() protected _config?: T; + + @property({ attribute: "in-grid", reflect: true, type: Boolean }) + protected _inGrid = false; + + public getCardSize(): number | Promise { + return 1; + } + + setConfig(config: T): void { + this._config = config; + } + + public getGridSize(): [number, number] { + this._inGrid = true; + let column = 2; + let row = 1; + if (!this._config) return [column, row]; + + const appearance = computeAppearance(this._config); + if (appearance.layout === "vertical") { + row += 1; + } + if (appearance.layout === "horizontal") { + column = 4; + } + return [column, row]; + } + protected renderPicture(picture: string): TemplateResult { return html` Date: Tue, 5 Mar 2024 13:28:29 +0100 Subject: [PATCH 06/25] Add grid support to vacuum card --- src/cards/climate-card/climate-card.ts | 40 ++-------- src/cards/light-card/light-card.ts | 11 +-- .../media-player-card/media-player-card.ts | 23 ------ .../controls/vacuum-commands-control.ts | 76 +++++++++++-------- src/cards/vacuum-card/vacuum-card.ts | 29 +++++-- src/utils/base-card.ts | 13 ++-- 6 files changed, 86 insertions(+), 106 deletions(-) diff --git a/src/cards/climate-card/climate-card.ts b/src/cards/climate-card/climate-card.ts index 135d9daea..18d783fe3 100644 --- a/src/cards/climate-card/climate-card.ts +++ b/src/cards/climate-card/climate-card.ts @@ -51,7 +51,7 @@ registerCustomCard({ }); @customElement(CLIMATE_CARD_NAME) -export class ClimateCard extends MushroomBaseCard implements LovelaceCard { +export class ClimateCard extends MushroomBaseCard implements LovelaceCard { public static async getConfigElement(): Promise { await import("./climate-card-editor"); return document.createElement(CLIMATE_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -66,12 +66,8 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: ClimateCardConfig; - @state() private _activeControl?: ClimateCardControl; - @state() private _inGrid = false; - private get _controls(): ClimateCardControl[] { if (!this._config || !this.hass || !this._config.entity) return []; @@ -89,31 +85,16 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { return controls; } - public getCardSize(): number | Promise { - return 1; - } - public getGridSize(): [number, number] { - this._inGrid = true; - let column = 2; - let row = 1; - if (!this._config) return [column, row]; - - const appearance = computeAppearance(this._config); - if (appearance.layout === "vertical") { - row += 1; - } - if (appearance.layout === "horizontal") { - column = 4; - } + const size = super.getGridSize(); if ( this._controls.length && !this._config?.collapsible_controls && - appearance.layout !== "horizontal" + this._appearance?.layout !== "horizontal" ) { - row += 1; + size[1] += 1; } - return [column, row]; + return size; } _onControlTap(ctrl, e): void { @@ -122,7 +103,7 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { } setConfig(config: ClimateCardConfig): void { - this._config = { + super.setConfig({ tap_action: { action: "toggle", }, @@ -130,7 +111,7 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { action: "more-info", }, ...config, - }; + }); this.updateActiveControl(); } @@ -192,12 +173,7 @@ export class ClimateCard extends MushroomBaseCard implements LovelaceCard { (!this._config.collapsible_controls || isActive(stateObj)) && this._controls.length; return html` - + implements Love public getGridSize(): [number, number] { const size = super.getGridSize(); - if (!this._config) return size; - const appearance = computeAppearance(this._config); if ( this._controls.length && !this._config?.collapsible_controls && - appearance.layout !== "horizontal" + this._appearance?.layout !== "horizontal" ) { size[1] += 1; } @@ -203,12 +201,7 @@ export class LightCard extends MushroomBaseCard implements Love (!this._config.collapsible_controls || isActive(stateObj)) && this._controls.length; return html` - + boolean; + command: VacuumCommand; + isSupported: (entity: VacuumEntity) => boolean; + isVisible?: (entity: VacuumEntity) => boolean; isDisabled: (entity: VacuumEntity) => boolean; } +export const isButtonVisible = ( + entity: VacuumEntity, + button: VacuumButton, + commands: VacuumCommand[] +) => isButtonSupported(entity, button, commands) && (!button.isVisible || button.isVisible(entity)); + +export const isButtonSupported = ( + entity: VacuumEntity, + button: VacuumButton, + commands: VacuumCommand[] +) => button.isSupported(entity) && commands.includes(button.command); + export const isCommandsControlVisible = (entity: VacuumEntity, commands: VacuumCommand[]) => - VACUUM_BUTTONS.some((item) => item.isVisible(entity, commands)); + VACUUM_BUTTONS.some((button) => isButtonVisible(entity, button, commands)); + +export const isCommandsControlSupported = (entity: VacuumEntity, commands: VacuumCommand[]) => + VACUUM_BUTTONS.some((button) => isButtonSupported(entity, button, commands)); export const VACUUM_BUTTONS: VacuumButton[] = [ { icon: "mdi:power", serviceName: "turn_on", - isVisible: (entity, commands) => - supportsFeature(entity, VACUUM_SUPPORT_TURN_ON) && - commands.includes("on_off") && - !isActive(entity), + command: "on_off", + isSupported: (entity) => supportsFeature(entity, VACUUM_SUPPORT_TURN_ON), + isVisible: (entity) => !isActive(entity), isDisabled: () => false, }, { icon: "mdi:power", serviceName: "turn_off", - isVisible: (entity, commands) => - supportsFeature(entity, VACUUM_SUPPORT_TURN_OFF) && - commands.includes("on_off") && - isActive(entity), + command: "on_off", + isSupported: (entity) => supportsFeature(entity, VACUUM_SUPPORT_TURN_OFF), + isVisible: (entity) => isActive(entity), isDisabled: () => false, }, { icon: "mdi:play", serviceName: "start", - isVisible: (entity, commands) => - supportsFeature(entity, VACUUM_SUPPORT_START) && - commands.includes("start_pause") && - !isCleaning(entity), + command: "start_pause", + isSupported: (entity) => supportsFeature(entity, VACUUM_SUPPORT_START), + isVisible: (entity) => !isCleaning(entity), isDisabled: () => false, }, { icon: "mdi:pause", serviceName: "pause", - isVisible: (entity, commands) => + command: "start_pause", + isSupported: (entity) => // We need also to check if Start is supported because if not we show play-pause supportsFeature(entity, VACUUM_SUPPORT_START) && - supportsFeature(entity, VACUUM_SUPPORT_PAUSE) && - commands.includes("start_pause") && - isCleaning(entity), + supportsFeature(entity, VACUUM_SUPPORT_PAUSE), + isVisible: (entity) => isCleaning(entity), isDisabled: () => false, }, { icon: "mdi:play-pause", serviceName: "start_pause", - isVisible: (entity, commands) => + command: "start_pause", + isSupported: (entity) => // If start is supported, we don't show this button !supportsFeature(entity, VACUUM_SUPPORT_START) && - supportsFeature(entity, VACUUM_SUPPORT_PAUSE) && - commands.includes("start_pause"), + supportsFeature(entity, VACUUM_SUPPORT_PAUSE), isDisabled: () => false, }, { icon: "mdi:stop", serviceName: "stop", - isVisible: (entity, commands) => - supportsFeature(entity, VACUUM_SUPPORT_STOP) && commands.includes("stop"), + command: "stop", + isSupported: (entity) => supportsFeature(entity, VACUUM_SUPPORT_STOP), isDisabled: (entity) => isStopped(entity), }, { icon: "mdi:target-variant", serviceName: "clean_spot", - isVisible: (entity, commands) => - supportsFeature(entity, VACUUM_SUPPORT_CLEAN_SPOT) && commands.includes("clean_spot"), + command: "clean_spot", + isSupported: (entity) => supportsFeature(entity, VACUUM_SUPPORT_CLEAN_SPOT), isDisabled: () => false, }, { icon: "mdi:map-marker", serviceName: "locate", - isVisible: (entity, commands) => - supportsFeature(entity, VACUUM_SUPPORT_LOCATE) && commands.includes("locate"), + command: "locate", + isSupported: (entity) => supportsFeature(entity, VACUUM_SUPPORT_LOCATE), isDisabled: (entity) => isReturningHome(entity), }, { icon: "mdi:home-map-marker", serviceName: "return_to_base", - isVisible: (entity, commands) => - supportsFeature(entity, VACUUM_SUPPORT_RETURN_HOME) && commands.includes("return_home"), + command: "return_home", + isSupported: (entity) => supportsFeature(entity, VACUUM_SUPPORT_RETURN_HOME), isDisabled: () => false, }, ]; @@ -131,7 +145,9 @@ export class CoverButtonsControl extends LitElement { return html` - ${VACUUM_BUTTONS.filter((item) => item.isVisible(this.entity, this.commands)).map( + ${VACUUM_BUTTONS.filter((item) => + isButtonVisible(this.entity, item, this.commands) + ).map( (item) => html` implements LovelaceCard { public static async getConfigElement(): Promise { await import("./vacuum-card-editor"); return document.createElement(VACUUM_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -53,14 +56,26 @@ export class VacuumCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: VacuumCardConfig; + isControlSupported() { + if (!this._config || !this.hass || !this._config.entity) return false; + + const entityId = this._config.entity; + const stateObj = this.hass.states[entityId] as VacuumEntity | undefined; + if (!stateObj) return false; + + return isCommandsControlSupported(stateObj, this._config.commands ?? []); + } - getCardSize(): number | Promise { - return 1; + public getGridSize(): [number, number] { + const size = super.getGridSize(); + if (this.isControlSupported() && this._appearance?.layout !== "horizontal") { + size[1] += 1; + } + return size; } setConfig(config: VacuumCardConfig): void { - this._config = { + super.setConfig({ tap_action: { action: "more-info", }, @@ -68,7 +83,7 @@ export class VacuumCard extends MushroomBaseCard implements LovelaceCard { action: "more-info", }, ...config, - }; + }); } private _handleAction(ev: ActionHandlerEvent) { diff --git a/src/utils/base-card.ts b/src/utils/base-card.ts index 4db029a3b..d920601e1 100644 --- a/src/utils/base-card.ts +++ b/src/utils/base-card.ts @@ -36,17 +36,20 @@ export class MushroomBaseCard extends Mushroo this._config = config; } + public get _appearance(): Appearance | undefined { + if (!this._config) return undefined; + return computeAppearance(this._config); + } + public getGridSize(): [number, number] { this._inGrid = true; let column = 2; let row = 1; - if (!this._config) return [column, row]; - - const appearance = computeAppearance(this._config); - if (appearance.layout === "vertical") { + if (!this._appearance) return [column, row]; + if (this._appearance.layout === "vertical") { row += 1; } - if (appearance.layout === "horizontal") { + if (this._appearance.layout === "horizontal") { column = 4; } return [column, row]; From f61e116582b17065b1c1eb55dfa4e3d08b8cef64 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 15:04:35 +0100 Subject: [PATCH 07/25] Refactor stateObj --- src/cards/climate-card/climate-card.ts | 28 ++++++--------------- src/cards/light-card/light-card.ts | 21 ++++------------ src/cards/update-card/update-card.ts | 25 ++++++++++-------- src/cards/vacuum-card/vacuum-card.ts | 31 ++++++++--------------- src/utils/base-card.ts | 35 +++++++++++++++++++------- 5 files changed, 63 insertions(+), 77 deletions(-) diff --git a/src/cards/climate-card/climate-card.ts b/src/cards/climate-card/climate-card.ts index 18d783fe3..0f1841a4a 100644 --- a/src/cards/climate-card/climate-card.ts +++ b/src/cards/climate-card/climate-card.ts @@ -51,7 +51,7 @@ registerCustomCard({ }); @customElement(CLIMATE_CARD_NAME) -export class ClimateCard extends MushroomBaseCard implements LovelaceCard { +export class ClimateCard extends MushroomBaseCard implements LovelaceCard { public static async getConfigElement(): Promise { await import("./climate-card-editor"); return document.createElement(CLIMATE_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -69,12 +69,9 @@ export class ClimateCard extends MushroomBaseCard implements @state() private _activeControl?: ClimateCardControl; private get _controls(): ClimateCardControl[] { - if (!this._config || !this.hass || !this._config.entity) return []; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as ClimateEntity | undefined; - if (!stateObj) return []; - + if (!this._config || !this._stateObj) return []; + + const stateObj = this._stateObj; const controls: ClimateCardControl[] = []; if (isTemperatureControlVisible(stateObj) && this._config.show_temperature_control) { controls.push("temperature_control"); @@ -85,16 +82,8 @@ export class ClimateCard extends MushroomBaseCard implements return controls; } - public getGridSize(): [number, number] { - const size = super.getGridSize(); - if ( - this._controls.length && - !this._config?.collapsible_controls && - this._appearance?.layout !== "horizontal" - ) { - size[1] += 1; - } - return size; + protected get hasControls(): boolean { + return this._controls.length > 0; } _onControlTap(ctrl, e): void { @@ -134,12 +123,11 @@ export class ClimateCard extends MushroomBaseCard implements } protected render() { - if (!this.hass || !this._config || !this._config.entity) { + if (!this.hass || !this._config) { return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as ClimateEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); diff --git a/src/cards/light-card/light-card.ts b/src/cards/light-card/light-card.ts index a78a930d2..cf4fad801 100644 --- a/src/cards/light-card/light-card.ts +++ b/src/cards/light-card/light-card.ts @@ -59,7 +59,7 @@ registerCustomCard({ }); @customElement(LIGHT_CARD_NAME) -export class LightCard extends MushroomBaseCard implements LovelaceCard { +export class LightCard extends MushroomBaseCard implements LovelaceCard { public static async getConfigElement(): Promise { await import("./light-card-editor"); return document.createElement(LIGHT_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -79,12 +79,9 @@ export class LightCard extends MushroomBaseCard implements Love @state() private brightness?: number; private get _controls(): LightCardControl[] { - if (!this._config || !this.hass || !this._config.entity) return []; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as LightEntity | undefined; - if (!stateObj) return []; + if (!this._config || !this._stateObj) return []; + const stateObj = this._stateObj; const controls: LightCardControl[] = []; if (this._config.show_brightness_control && supportsBrightnessControl(stateObj)) { controls.push("brightness_control"); @@ -98,16 +95,8 @@ export class LightCard extends MushroomBaseCard implements Love return controls; } - public getGridSize(): [number, number] { - const size = super.getGridSize(); - if ( - this._controls.length && - !this._config?.collapsible_controls && - this._appearance?.layout !== "horizontal" - ) { - size[1] += 1; - } - return size; + protected get hasControls(): boolean { + return this._controls.length > 0; } setConfig(config: LightCardConfig): void { diff --git a/src/cards/update-card/update-card.ts b/src/cards/update-card/update-card.ts index e7b0f863f..a3d0f0566 100644 --- a/src/cards/update-card/update-card.ts +++ b/src/cards/update-card/update-card.ts @@ -40,7 +40,10 @@ registerCustomCard({ }); @customElement(UPDATE_CARD_NAME) -export class UpdateCard extends MushroomBaseCard implements LovelaceCard { +export class UpdateCard + extends MushroomBaseCard + implements LovelaceCard +{ public static async getConfigElement(): Promise { await import("./update-card-editor"); return document.createElement(UPDATE_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -55,14 +58,15 @@ export class UpdateCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: UpdateCardConfig; - - getCardSize(): number | Promise { - return 1; + protected get hasControls() { + if (!this._stateObj || !this._config) return false; + return ( + Boolean(this._config.show_buttons_control) && + supportsFeature(this._stateObj, UPDATE_SUPPORT_INSTALL) + ); } - setConfig(config: UpdateCardConfig): void { - this._config = { + super.setConfig({ tap_action: { action: "more-info", }, @@ -70,7 +74,7 @@ export class UpdateCard extends MushroomBaseCard implements LovelaceCard { action: "more-info", }, ...config, - }; + }); } private _handleAction(ev: ActionHandlerEvent) { @@ -78,12 +82,11 @@ export class UpdateCard extends MushroomBaseCard implements LovelaceCard { } protected render() { - if (!this._config || !this.hass || !this._config.entity) { + if (!this._config || !this.hass) { return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as UpdateEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); diff --git a/src/cards/vacuum-card/vacuum-card.ts b/src/cards/vacuum-card/vacuum-card.ts index e135a974e..7e08cedda 100644 --- a/src/cards/vacuum-card/vacuum-card.ts +++ b/src/cards/vacuum-card/vacuum-card.ts @@ -41,7 +41,10 @@ registerCustomCard({ }); @customElement(VACUUM_CARD_NAME) -export class VacuumCard extends MushroomBaseCard implements LovelaceCard { +export class VacuumCard + extends MushroomBaseCard + implements LovelaceCard +{ public static async getConfigElement(): Promise { await import("./vacuum-card-editor"); return document.createElement(VACUUM_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -56,22 +59,9 @@ export class VacuumCard extends MushroomBaseCard implements Lo }; } - isControlSupported() { - if (!this._config || !this.hass || !this._config.entity) return false; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as VacuumEntity | undefined; - if (!stateObj) return false; - - return isCommandsControlSupported(stateObj, this._config.commands ?? []); - } - - public getGridSize(): [number, number] { - const size = super.getGridSize(); - if (this.isControlSupported() && this._appearance?.layout !== "horizontal") { - size[1] += 1; - } - return size; + protected get hasControls() { + if (!this._stateObj || !this._config) return false; + return isCommandsControlSupported(this._stateObj, this._config.commands ?? []); } setConfig(config: VacuumCardConfig): void { @@ -91,12 +81,11 @@ export class VacuumCard extends MushroomBaseCard implements Lo } protected render() { - if (!this._config || !this.hass || !this._config.entity) { + if (!this._config || !this.hass) { return nothing; } - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as VacuumEntity | undefined; + + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); diff --git a/src/utils/base-card.ts b/src/utils/base-card.ts index d920601e1..9a6acdfc0 100644 --- a/src/utils/base-card.ts +++ b/src/utils/base-card.ts @@ -22,12 +22,26 @@ export function computeDarkMode(hass?: HomeAssistant): boolean { if (!hass) return false; return (hass.themes as any).darkMode as boolean; } -export class MushroomBaseCard extends MushroomBaseElement { +export class MushroomBaseCard< + T extends BaseConfig = BaseConfig, + E extends HassEntity = HassEntity, +> extends MushroomBaseElement { @state() protected _config?: T; @property({ attribute: "in-grid", reflect: true, type: Boolean }) protected _inGrid = false; + protected get _stateObj(): E | undefined { + if (!this._config || !this.hass || !this._config.entity) return undefined; + + const entityId = this._config.entity; + return this.hass.states[entityId] as E; + } + + protected get hasControls(): boolean { + return false; + } + public getCardSize(): number | Promise { return 1; } @@ -36,22 +50,25 @@ export class MushroomBaseCard extends Mushroo this._config = config; } - public get _appearance(): Appearance | undefined { - if (!this._config) return undefined; - return computeAppearance(this._config); - } - public getGridSize(): [number, number] { this._inGrid = true; let column = 2; let row = 1; - if (!this._appearance) return [column, row]; - if (this._appearance.layout === "vertical") { + if (!this._config) return [column, row]; + const appearance = computeAppearance(this._config); + if (appearance.layout === "vertical") { row += 1; } - if (this._appearance.layout === "horizontal") { + if (appearance.layout === "horizontal") { column = 4; } + if ( + appearance?.layout !== "horizontal" && + this.hasControls && + !("collapsible_controls" in this._config && this._config?.collapsible_controls) + ) { + row += 1; + } return [column, row]; } From e6d610f23f475b18bef62bdf07c13edeaecfba5d Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 15:07:42 +0100 Subject: [PATCH 08/25] Add grid support to select card --- src/cards/select-card/select-card.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/cards/select-card/select-card.ts b/src/cards/select-card/select-card.ts index 416ae4abd..e8b906b21 100644 --- a/src/cards/select-card/select-card.ts +++ b/src/cards/select-card/select-card.ts @@ -37,7 +37,7 @@ registerCustomCard({ }); @customElement(SELECT_CARD_NAME) -export class SelectCard extends MushroomBaseCard implements LovelaceCard { +export class SelectCard extends MushroomBaseCard implements LovelaceCard { public static async getConfigElement(): Promise { await import("./select-card-editor"); return document.createElement(SELECT_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -52,14 +52,12 @@ export class SelectCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: SelectCardConfig; - - getCardSize(): number | Promise { - return 1; + protected get hasControls(): boolean { + return true; } setConfig(config: SelectCardConfig): void { - this._config = { + super.setConfig({ tap_action: { action: "more-info", }, @@ -67,7 +65,7 @@ export class SelectCard extends MushroomBaseCard implements LovelaceCard { action: "more-info", }, ...config, - }; + }); } private _handleAction(ev: ActionHandlerEvent) { @@ -79,8 +77,7 @@ export class SelectCard extends MushroomBaseCard implements LovelaceCard { return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as HassEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); From b14646f5917e8829c2f8acb0a9153080473149f8 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 15:09:29 +0100 Subject: [PATCH 09/25] Add grid support to template card --- src/cards/template-card/template-card.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/cards/template-card/template-card.ts b/src/cards/template-card/template-card.ts index 5b4d15584..dacdefb9c 100644 --- a/src/cards/template-card/template-card.ts +++ b/src/cards/template-card/template-card.ts @@ -1,6 +1,6 @@ import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, nothing, PropertyValues, TemplateResult } from "lit"; -import { customElement, state } from "lit/decorators.js"; +import { customElement, property, state } from "lit/decorators.js"; import { classMap } from "lit/directives/class-map.js"; import { styleMap } from "lit/directives/style-map.js"; import { @@ -69,7 +69,8 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard { @state() private _unsubRenderTemplates: Map> = new Map(); - @state() private _inGrid = false; + @property({ attribute: "in-grid", reflect: true, type: Boolean }) + protected _inGrid = false; public getCardSize(): number | Promise { return 1; @@ -160,12 +161,7 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard { const weatherSvg = getWeatherSvgIcon(icon); return html` - + Date: Tue, 5 Mar 2024 15:11:31 +0100 Subject: [PATCH 10/25] Default set config --- src/cards/climate-card/climate-card.ts | 3 --- src/cards/entity-card/entity-card.ts | 12 ------------ src/cards/light-card/light-card.ts | 3 --- src/cards/select-card/select-card.ts | 12 ------------ src/cards/update-card/update-card.ts | 11 ----------- src/cards/vacuum-card/vacuum-card.ts | 12 ------------ src/utils/base-card.ts | 10 +++++++++- 7 files changed, 9 insertions(+), 54 deletions(-) diff --git a/src/cards/climate-card/climate-card.ts b/src/cards/climate-card/climate-card.ts index 0f1841a4a..11777cc1f 100644 --- a/src/cards/climate-card/climate-card.ts +++ b/src/cards/climate-card/climate-card.ts @@ -96,9 +96,6 @@ export class ClimateCard extends MushroomBaseCard implements Lo }; } - setConfig(config: EntityCardConfig): void { - super.setConfig({ - tap_action: { - action: "more-info", - }, - hold_action: { - action: "more-info", - }, - ...config, - }); - } - private _handleAction(ev: ActionHandlerEvent) { handleAction(this, this.hass!, this._config!, ev.detail.action!); } diff --git a/src/cards/light-card/light-card.ts b/src/cards/light-card/light-card.ts index cf4fad801..a0e5fa525 100644 --- a/src/cards/light-card/light-card.ts +++ b/src/cards/light-card/light-card.ts @@ -104,9 +104,6 @@ export class LightCard extends MushroomBaseCard im tap_action: { action: "toggle", }, - hold_action: { - action: "more-info", - }, ...config, }); this.updateActiveControls(); diff --git a/src/cards/select-card/select-card.ts b/src/cards/select-card/select-card.ts index e8b906b21..91cc83f5d 100644 --- a/src/cards/select-card/select-card.ts +++ b/src/cards/select-card/select-card.ts @@ -56,18 +56,6 @@ export class SelectCard extends MushroomBaseCard implements Lo return true; } - setConfig(config: SelectCardConfig): void { - super.setConfig({ - tap_action: { - action: "more-info", - }, - hold_action: { - action: "more-info", - }, - ...config, - }); - } - private _handleAction(ev: ActionHandlerEvent) { handleAction(this, this.hass!, this._config!, ev.detail.action!); } diff --git a/src/cards/update-card/update-card.ts b/src/cards/update-card/update-card.ts index a3d0f0566..9e300a09c 100644 --- a/src/cards/update-card/update-card.ts +++ b/src/cards/update-card/update-card.ts @@ -65,17 +65,6 @@ export class UpdateCard supportsFeature(this._stateObj, UPDATE_SUPPORT_INSTALL) ); } - setConfig(config: UpdateCardConfig): void { - super.setConfig({ - tap_action: { - action: "more-info", - }, - hold_action: { - action: "more-info", - }, - ...config, - }); - } private _handleAction(ev: ActionHandlerEvent) { handleAction(this, this.hass!, this._config!, ev.detail.action!); diff --git a/src/cards/vacuum-card/vacuum-card.ts b/src/cards/vacuum-card/vacuum-card.ts index 7e08cedda..1869c83d3 100644 --- a/src/cards/vacuum-card/vacuum-card.ts +++ b/src/cards/vacuum-card/vacuum-card.ts @@ -64,18 +64,6 @@ export class VacuumCard return isCommandsControlSupported(this._stateObj, this._config.commands ?? []); } - setConfig(config: VacuumCardConfig): void { - super.setConfig({ - tap_action: { - action: "more-info", - }, - hold_action: { - action: "more-info", - }, - ...config, - }); - } - private _handleAction(ev: ActionHandlerEvent) { handleAction(this, this.hass!, this._config!, ev.detail.action!); } diff --git a/src/utils/base-card.ts b/src/utils/base-card.ts index 9a6acdfc0..806d82f14 100644 --- a/src/utils/base-card.ts +++ b/src/utils/base-card.ts @@ -47,7 +47,15 @@ export class MushroomBaseCard< } setConfig(config: T): void { - this._config = config; + this._config = { + tap_action: { + action: "more-info", + }, + hold_action: { + action: "more-info", + }, + ...config, + }; } public getGridSize(): [number, number] { From 425f16b161a6663267ff5cf2a1805bbfb06680ba Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 15:13:44 +0100 Subject: [PATCH 11/25] Add grid support to person card --- src/cards/entity-card/entity-card.ts | 5 ++--- src/cards/person-card/person-card.ts | 25 +++---------------------- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/cards/entity-card/entity-card.ts b/src/cards/entity-card/entity-card.ts index acd9290b0..da9cc9cca 100644 --- a/src/cards/entity-card/entity-card.ts +++ b/src/cards/entity-card/entity-card.ts @@ -1,6 +1,6 @@ import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, nothing, TemplateResult } from "lit"; -import { customElement, state } from "lit/decorators.js"; +import { customElement } from "lit/decorators.js"; import { classMap } from "lit/directives/class-map.js"; import { styleMap } from "lit/directives/style-map.js"; import { @@ -59,8 +59,7 @@ export class EntityCard extends MushroomBaseCard implements Lo return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as HassEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); diff --git a/src/cards/person-card/person-card.ts b/src/cards/person-card/person-card.ts index 969bab5e2..8cef3d0ba 100644 --- a/src/cards/person-card/person-card.ts +++ b/src/cards/person-card/person-card.ts @@ -1,6 +1,6 @@ import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, nothing } from "lit"; -import { customElement, state } from "lit/decorators.js"; +import { customElement } from "lit/decorators.js"; import { classMap } from "lit/directives/class-map.js"; import { styleMap } from "lit/directives/style-map.js"; import { @@ -36,7 +36,7 @@ registerCustomCard({ }); @customElement(PERSON_CARD_NAME) -export class PersonCard extends MushroomBaseCard implements LovelaceCard { +export class PersonCard extends MushroomBaseCard implements LovelaceCard { public static async getConfigElement(): Promise { await import("./person-card-editor"); return document.createElement(PERSON_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -51,24 +51,6 @@ export class PersonCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: PersonCardConfig; - - getCardSize(): number | Promise { - return 1; - } - - setConfig(config: PersonCardConfig): void { - this._config = { - tap_action: { - action: "more-info", - }, - hold_action: { - action: "more-info", - }, - ...config, - }; - } - private _handleAction(ev: ActionHandlerEvent) { handleAction(this, this.hass!, this._config!, ev.detail.action!); } @@ -78,8 +60,7 @@ export class PersonCard extends MushroomBaseCard implements LovelaceCard { return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as HassEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); From fd478cc63d5886c882f4e2ff080c97aea1331647 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 15:16:02 +0100 Subject: [PATCH 12/25] Add grid support to number card --- src/cards/number-card/number-card.ts | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/cards/number-card/number-card.ts b/src/cards/number-card/number-card.ts index a653f70e5..2612007fe 100644 --- a/src/cards/number-card/number-card.ts +++ b/src/cards/number-card/number-card.ts @@ -42,7 +42,7 @@ registerCustomCard({ }); @customElement(NUMBER_CARD_NAME) -export class NumberCard extends MushroomBaseCard implements LovelaceCard { +export class NumberCard extends MushroomBaseCard implements LovelaceCard { public static async getConfigElement(): Promise { await import("./number-card-editor"); return document.createElement(NUMBER_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -57,25 +57,11 @@ export class NumberCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: NumberCardConfig; - - @state() private value?: number; - - getCardSize(): number | Promise { - return 1; + protected get hasControls(): boolean { + return true; } - setConfig(config: NumberCardConfig): void { - this._config = { - tap_action: { - action: "more-info", - }, - hold_action: { - action: "more-info", - }, - ...config, - }; - } + @state() private value?: number; private _handleAction(ev: ActionHandlerEvent) { handleAction(this, this.hass!, this._config!, ev.detail.action!); From 41d84baf24649e31f67f4fc2bbc24cece6f6c264 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 15:21:55 +0100 Subject: [PATCH 13/25] Add grid support to media player card --- .../media-player-card/media-player-card.ts | 45 ++++++++----------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/src/cards/media-player-card/media-player-card.ts b/src/cards/media-player-card/media-player-card.ts index 12c8183c7..2e5c82f0d 100644 --- a/src/cards/media-player-card/media-player-card.ts +++ b/src/cards/media-player-card/media-player-card.ts @@ -55,7 +55,10 @@ registerCustomCard({ }); @customElement(MEDIA_PLAYER_CARD_NAME) -export class MediaPlayerCard extends MushroomBaseCard implements LovelaceCard { +export class MediaPlayerCard + extends MushroomBaseCard + implements LovelaceCard +{ public static async getConfigElement(): Promise { await import("./media-player-card-editor"); return document.createElement(MEDIA_PLAYER_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -72,18 +75,19 @@ export class MediaPlayerCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: MediaPlayerCardConfig; - @state() private _activeControl?: MediaPlayerCardControl; - @state() private _inGrid = false; + protected get hasControls(): boolean { + return ( + Boolean(this._config?.media_controls?.length) || + Boolean(this._config?.volume_controls?.length) + ); + } private get _controls(): MediaPlayerCardControl[] { - if (!this._config || !this.hass || !this._config.entity) return []; + if (!this._config || !this._stateObj) return []; - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as MediaPlayerEntity | undefined; - if (!stateObj) return []; + const stateObj = this._stateObj; const controls: MediaPlayerCardControl[] = []; if (isMediaControlVisible(stateObj, this._config.media_controls)) { @@ -95,33 +99,21 @@ export class MediaPlayerCard extends MushroomBaseCard implements LovelaceCard { return controls; } - public getCardSize(): number | Promise { - return 1; - } - _onControlTap(ctrl, e): void { e.stopPropagation(); this._activeControl = ctrl; } setConfig(config: MediaPlayerCardConfig): void { - this._config = { - tap_action: { - action: "more-info", - }, - hold_action: { - action: "more-info", - }, - ...config, - }; - this.updateControls(); + super.setConfig(config); + this.updateActiveControls(); this.updateVolume(); } protected updated(changedProperties: PropertyValues) { super.updated(changedProperties); if (this.hass && changedProperties.has("hass")) { - this.updateControls(); + this.updateActiveControls(); this.updateVolume(); } } @@ -147,7 +139,7 @@ export class MediaPlayerCard extends MushroomBaseCard implements LovelaceCard { } } - updateControls() { + updateActiveControls() { const isActiveControlSupported = this._activeControl ? this._controls.includes(this._activeControl) : false; @@ -162,9 +154,8 @@ export class MediaPlayerCard extends MushroomBaseCard implements LovelaceCard { if (!this._config || !this.hass || !this._config.entity) { return nothing; } - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as MediaPlayerEntity | undefined; + + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); From e09dbd5db366d363bf5b3e5d4eb1018b829e12da Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 15:25:39 +0100 Subject: [PATCH 14/25] Add grid support to lock card --- src/cards/lock-card/lock-card.ts | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/cards/lock-card/lock-card.ts b/src/cards/lock-card/lock-card.ts index fc1d9ade5..18c3d65a5 100644 --- a/src/cards/lock-card/lock-card.ts +++ b/src/cards/lock-card/lock-card.ts @@ -37,7 +37,7 @@ registerCustomCard({ }); @customElement(LOCK_CARD_NAME) -export class LockCard extends MushroomBaseCard implements LovelaceCard { +export class LockCard extends MushroomBaseCard implements LovelaceCard { public static async getConfigElement(): Promise { await import("./lock-card-editor"); return document.createElement(LOCK_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -52,22 +52,8 @@ export class LockCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: LockCardConfig; - - getCardSize(): number | Promise { - return 1; - } - - setConfig(config: LockCardConfig): void { - this._config = { - tap_action: { - action: "more-info", - }, - hold_action: { - action: "more-info", - }, - ...config, - }; + protected get hasControls(): boolean { + return true; } private _handleAction(ev: ActionHandlerEvent) { From 802365ef68709f84bd70177aa72cbfaedc276408 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 15:28:45 +0100 Subject: [PATCH 15/25] Add grid support to humidifier card --- src/cards/humidifier-card/humidifier-card.ts | 18 +++++++++--------- src/cards/lock-card/lock-card.ts | 3 +-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/cards/humidifier-card/humidifier-card.ts b/src/cards/humidifier-card/humidifier-card.ts index c21e70643..a415d9ff4 100644 --- a/src/cards/humidifier-card/humidifier-card.ts +++ b/src/cards/humidifier-card/humidifier-card.ts @@ -42,7 +42,10 @@ registerCustomCard({ }); @customElement(HUMIDIFIER_CARD_NAME) -export class HumidifierCard extends MushroomBaseCard implements LovelaceCard { +export class HumidifierCard + extends MushroomBaseCard + implements LovelaceCard +{ public static async getConfigElement(): Promise { await import("./humidifier-card-editor"); return document.createElement(HUMIDIFIER_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -59,16 +62,14 @@ export class HumidifierCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: HumidifierCardConfig; - @state() private humidity?: number; - getCardSize(): number | Promise { - return 1; + protected get hasControls(): boolean { + return Boolean(this._config?.show_target_humidity_control); } setConfig(config: HumidifierCardConfig): void { - this._config = { + super.setConfig({ tap_action: { action: "toggle", }, @@ -76,7 +77,7 @@ export class HumidifierCard extends MushroomBaseCard implements LovelaceCard { action: "more-info", }, ...config, - }; + }); } private _handleAction(ev: ActionHandlerEvent) { @@ -94,8 +95,7 @@ export class HumidifierCard extends MushroomBaseCard implements LovelaceCard { return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as HumidifierEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); diff --git a/src/cards/lock-card/lock-card.ts b/src/cards/lock-card/lock-card.ts index 18c3d65a5..5c2f711a9 100644 --- a/src/cards/lock-card/lock-card.ts +++ b/src/cards/lock-card/lock-card.ts @@ -65,8 +65,7 @@ export class LockCard extends MushroomBaseCard imple return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as LockEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); From 737219fe990942f01a47d1cb13146ffb0bcf86fb Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 16:40:40 +0100 Subject: [PATCH 16/25] Add grid support to fan card --- src/cards/fan-card/fan-card.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/cards/fan-card/fan-card.ts b/src/cards/fan-card/fan-card.ts index 92edea9b1..44a9f3ebf 100644 --- a/src/cards/fan-card/fan-card.ts +++ b/src/cards/fan-card/fan-card.ts @@ -41,7 +41,7 @@ registerCustomCard({ }); @customElement(FAN_CARD_NAME) -export class FanCard extends MushroomBaseCard implements LovelaceCard { +export class FanCard extends MushroomBaseCard implements LovelaceCard { public static async getConfigElement(): Promise { await import("./fan-card-editor"); return document.createElement(FAN_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -56,14 +56,15 @@ export class FanCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: FanCardConfig; - - getCardSize(): number | Promise { - return 1; + protected get hasControls(): boolean { + return ( + Boolean(this._config?.show_percentage_control) || + Boolean(this._config?.show_oscillate_control) + ); } setConfig(config: FanCardConfig): void { - this._config = { + super.setConfig({ tap_action: { action: "toggle", }, @@ -71,7 +72,7 @@ export class FanCard extends MushroomBaseCard implements LovelaceCard { action: "more-info", }, ...config, - }; + }); this.updatePercentage(); } @@ -87,12 +88,8 @@ export class FanCard extends MushroomBaseCard implements LovelaceCard { updatePercentage() { this.percentage = undefined; - if (!this._config || !this.hass || !this._config.entity) return; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as HassEntity | undefined; - - if (!stateObj) return; + const stateObj = this._stateObj; + if (!this._config || !this.hass || !stateObj) return; this.percentage = getPercentage(stateObj); } @@ -111,8 +108,7 @@ export class FanCard extends MushroomBaseCard implements LovelaceCard { return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as HassEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); From 47190b75caf8dcf75da044ccf0980f246523e361 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 16:49:44 +0100 Subject: [PATCH 17/25] Add grid support to cover card --- src/cards/cover-card/cover-card.ts | 39 +++++++++++++------ src/cards/light-card/light-card.ts | 11 ++++-- .../media-player-card/media-player-card.ts | 6 +-- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/cards/cover-card/cover-card.ts b/src/cards/cover-card/cover-card.ts index d647d1bb4..8b00bb623 100644 --- a/src/cards/cover-card/cover-card.ts +++ b/src/cards/cover-card/cover-card.ts @@ -51,7 +51,10 @@ registerCustomCard({ }); @customElement(COVER_CARD_NAME) -export class CoverCard extends MushroomBaseCard implements LovelaceCard { +export class CoverCard + extends MushroomBaseCard + implements LovelaceCard +{ public static async getConfigElement(): Promise { await import("./cover-card-editor"); return document.createElement(COVER_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -66,12 +69,12 @@ export class CoverCard extends MushroomBaseCard implements LovelaceCard { }; } - @state() private _config?: CoverCardConfig; + protected get hasControls(): boolean { + return this._controls.length > 0; + } @state() private _activeControl?: CoverCardControl; - @state() private _controls: CoverCardControl[] = []; - get _nextControl(): CoverCardControl | undefined { if (this._activeControl) { return ( @@ -91,7 +94,7 @@ export class CoverCard extends MushroomBaseCard implements LovelaceCard { } setConfig(config: CoverCardConfig): void { - this._config = { + super.setConfig({ tap_action: { action: "toggle", }, @@ -99,26 +102,38 @@ export class CoverCard extends MushroomBaseCard implements LovelaceCard { action: "more-info", }, ...config, - }; + }); + this.updateActiveControl(); + this.updatePosition(); + } + + private get _controls(): CoverCardControl[] { + if (!this._config || !this._stateObj) return []; const controls: CoverCardControl[] = []; - if (this._config?.show_buttons_control) { + if (this._config.show_buttons_control) { controls.push("buttons_control"); } - if (this._config?.show_position_control) { + if (this._config.show_position_control) { controls.push("position_control"); } - if (this._config?.show_tilt_position_control) { + if (this._config.show_tilt_position_control) { controls.push("tilt_position_control"); } - this._controls = controls; - this._activeControl = controls[0]; - this.updatePosition(); + return controls; + } + + updateActiveControl() { + const isActiveControlSupported = this._activeControl + ? this._controls.includes(this._activeControl) + : false; + this._activeControl = isActiveControlSupported ? this._activeControl : this._controls[0]; } protected updated(changedProperties: PropertyValues) { super.updated(changedProperties); if (this.hass && changedProperties.has("hass")) { this.updatePosition(); + this.updateActiveControl(); } } diff --git a/src/cards/light-card/light-card.ts b/src/cards/light-card/light-card.ts index a0e5fa525..ffc49655b 100644 --- a/src/cards/light-card/light-card.ts +++ b/src/cards/light-card/light-card.ts @@ -59,7 +59,10 @@ registerCustomCard({ }); @customElement(LIGHT_CARD_NAME) -export class LightCard extends MushroomBaseCard implements LovelaceCard { +export class LightCard + extends MushroomBaseCard + implements LovelaceCard +{ public static async getConfigElement(): Promise { await import("./light-card-editor"); return document.createElement(LIGHT_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -106,7 +109,7 @@ export class LightCard extends MushroomBaseCard im }, ...config, }); - this.updateActiveControls(); + this.updateActiveControl(); this.updateBrightness(); } @@ -118,7 +121,7 @@ export class LightCard extends MushroomBaseCard im protected updated(changedProperties: PropertyValues) { super.updated(changedProperties); if (this.hass && changedProperties.has("hass")) { - this.updateActiveControls(); + this.updateActiveControl(); this.updateBrightness(); } } @@ -140,7 +143,7 @@ export class LightCard extends MushroomBaseCard im } } - updateActiveControls() { + updateActiveControl() { const isActiveControlSupported = this._activeControl ? this._controls.includes(this._activeControl) : false; diff --git a/src/cards/media-player-card/media-player-card.ts b/src/cards/media-player-card/media-player-card.ts index 2e5c82f0d..304f9162f 100644 --- a/src/cards/media-player-card/media-player-card.ts +++ b/src/cards/media-player-card/media-player-card.ts @@ -106,14 +106,14 @@ export class MediaPlayerCard setConfig(config: MediaPlayerCardConfig): void { super.setConfig(config); - this.updateActiveControls(); + this.updateActiveControl(); this.updateVolume(); } protected updated(changedProperties: PropertyValues) { super.updated(changedProperties); if (this.hass && changedProperties.has("hass")) { - this.updateActiveControls(); + this.updateActiveControl(); this.updateVolume(); } } @@ -139,7 +139,7 @@ export class MediaPlayerCard } } - updateActiveControls() { + updateActiveControl() { const isActiveControlSupported = this._activeControl ? this._controls.includes(this._activeControl) : false; From 658a593172339959b99e932683b52f86da7b5a2f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 16:56:53 +0100 Subject: [PATCH 18/25] Add grid support to alarm control panel card --- .../alarm-control-panel-card.ts | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/cards/alarm-control-panel-card/alarm-control-panel-card.ts b/src/cards/alarm-control-panel-card/alarm-control-panel-card.ts index 9cd4dc462..4509ca5ee 100644 --- a/src/cards/alarm-control-panel-card/alarm-control-panel-card.ts +++ b/src/cards/alarm-control-panel-card/alarm-control-panel-card.ts @@ -62,7 +62,10 @@ const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "clear"]; */ @customElement(ALARM_CONTROl_PANEL_CARD_NAME) -export class AlarmControlPanelCard extends MushroomBaseCard implements LovelaceCard { +export class AlarmControlPanelCard + extends MushroomBaseCard + implements LovelaceCard +{ public static async getConfigElement(): Promise { await import("./alarm-control-panel-card-editor"); return document.createElement(ALARM_CONTROl_PANEL_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -80,24 +83,21 @@ export class AlarmControlPanelCard extends MushroomBaseCard implements LovelaceC }; } - @state() private _config?: AlarmControlPanelCardConfig; - - @query("#alarmCode") private _input?: HaTextField; + protected get hasControls(): boolean { + return Boolean(this._config?.states?.length); + } - getCardSize(): number | Promise { - return 1; + public getGridSize() { + if (this._config?.show_keypad) { + return [4, 1] as [number, number]; + } + return super.getGridSize(); } + @query("#alarmCode") private _input?: HaTextField; + setConfig(config: AlarmControlPanelCardConfig): void { - this._config = { - tap_action: { - action: "more-info", - }, - hold_action: { - action: "more-info", - }, - ...config, - }; + super.setConfig(config); this.loadComponents(); } @@ -144,9 +144,7 @@ export class AlarmControlPanelCard extends MushroomBaseCard implements LovelaceC } private get _hasCode(): boolean { - const entityId = this._config?.entity; - if (!entityId) return false; - const stateObj = this.hass.states[entityId] as HassEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) return false; return hasCode(stateObj) && Boolean(this._config?.show_keypad); } @@ -156,8 +154,7 @@ export class AlarmControlPanelCard extends MushroomBaseCard implements LovelaceC return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as HassEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); From d27eb911a0d0c89aaffb1e2efd50929191028265 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 17:00:24 +0100 Subject: [PATCH 19/25] Clean stateObj --- .../alarm-control-panel-card/alarm-control-panel-card.ts | 4 +--- src/cards/cover-card/cover-card.ts | 8 ++------ src/cards/light-card/light-card.ts | 8 ++------ src/cards/media-player-card/media-player-card.ts | 5 +---- src/cards/number-card/number-card.ts | 8 ++------ 5 files changed, 8 insertions(+), 25 deletions(-) diff --git a/src/cards/alarm-control-panel-card/alarm-control-panel-card.ts b/src/cards/alarm-control-panel-card/alarm-control-panel-card.ts index 4509ca5ee..b0d3bfc3c 100644 --- a/src/cards/alarm-control-panel-card/alarm-control-panel-card.ts +++ b/src/cards/alarm-control-panel-card/alarm-control-panel-card.ts @@ -109,9 +109,7 @@ export class AlarmControlPanelCard } async loadComponents() { - if (!this._config || !this.hass || !this._config.entity) return; - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as HassEntity | undefined; + const stateObj = this._stateObj; if (stateObj && hasCode(stateObj)) { void import("../../shared/form/mushroom-textfield"); diff --git a/src/cards/cover-card/cover-card.ts b/src/cards/cover-card/cover-card.ts index 8b00bb623..b4e1bfd22 100644 --- a/src/cards/cover-card/cover-card.ts +++ b/src/cards/cover-card/cover-card.ts @@ -142,10 +142,7 @@ export class CoverCard updatePosition() { this.position = undefined; - if (!this._config || !this.hass || !this._config.entity) return; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as CoverEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) return; this.position = getPosition(stateObj); @@ -166,8 +163,7 @@ export class CoverCard return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as CoverEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); diff --git a/src/cards/light-card/light-card.ts b/src/cards/light-card/light-card.ts index ffc49655b..1e40533c3 100644 --- a/src/cards/light-card/light-card.ts +++ b/src/cards/light-card/light-card.ts @@ -128,10 +128,7 @@ export class LightCard updateBrightness() { this.brightness = undefined; - if (!this._config || !this.hass || !this._config.entity) return; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as LightEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) return; this.brightness = getBrightness(stateObj); @@ -159,8 +156,7 @@ export class LightCard return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as LightEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); diff --git a/src/cards/media-player-card/media-player-card.ts b/src/cards/media-player-card/media-player-card.ts index 304f9162f..070e638d9 100644 --- a/src/cards/media-player-card/media-player-card.ts +++ b/src/cards/media-player-card/media-player-card.ts @@ -123,10 +123,7 @@ export class MediaPlayerCard updateVolume() { this.volume = undefined; - if (!this._config || !this.hass || !this._config.entity) return; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as MediaPlayerEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) return; const volume = getVolumeLevel(stateObj); diff --git a/src/cards/number-card/number-card.ts b/src/cards/number-card/number-card.ts index 2612007fe..e7f005e22 100644 --- a/src/cards/number-card/number-card.ts +++ b/src/cards/number-card/number-card.ts @@ -82,10 +82,7 @@ export class NumberCard extends MushroomBaseCard implements Lo updateValue() { this.value = undefined; - if (!this._config || !this.hass || !this._config.entity) return; - - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as HassEntity | undefined; + const stateObj =this._stateObj; if (!stateObj || Number.isNaN(stateObj.state)) return; this.value = Number(stateObj.state); @@ -96,8 +93,7 @@ export class NumberCard extends MushroomBaseCard implements Lo return nothing; } - const entityId = this._config.entity; - const stateObj = this.hass.states[entityId] as HassEntity | undefined; + const stateObj = this._stateObj; if (!stateObj) { return this.renderNotFound(this._config); From e500a84fdb24bc9aec781baf423ebee585335350 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 17:02:06 +0100 Subject: [PATCH 20/25] Prettier --- .../chips/conditional-chip-editor-legacy.ts | 15 +++++++-------- .../chips-card/chips/conditional-chip-editor.ts | 15 +++++++-------- src/cards/chips-card/chips/conditional-chip.ts | 2 +- src/cards/climate-card/climate-card.ts | 12 +++++++++--- .../fan-card/controls/fan-oscillate-control.ts | 4 +--- .../lock-card/controls/lock-buttons-control.ts | 2 +- .../controls/media-player-media-control.ts | 2 +- .../controls/media-player-volume-control.ts | 2 +- src/cards/media-player-card/media-player-card.ts | 2 +- src/cards/number-card/number-card.ts | 2 +- src/cards/vacuum-card/vacuum-card.ts | 2 +- src/shared/editor/layout-picker.ts | 13 ++++++------- 12 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/cards/chips-card/chips/conditional-chip-editor-legacy.ts b/src/cards/chips-card/chips/conditional-chip-editor-legacy.ts index bce8dd058..7a7ab0dbe 100644 --- a/src/cards/chips-card/chips/conditional-chip-editor-legacy.ts +++ b/src/cards/chips-card/chips/conditional-chip-editor-legacy.ts @@ -102,14 +102,13 @@ export class ConditionalChipEditor extends LitElement implements LovelaceChipEdi naturalMenuWidth > ${CHIP_LIST.map( - (chip) => - html` - - ${customLocalize( - `editor.chip.chip-picker.types.${chip}` - )} - - ` + (chip) => html` + + ${customLocalize( + `editor.chip.chip-picker.types.${chip}` + )} + + ` )} `} diff --git a/src/cards/chips-card/chips/conditional-chip-editor.ts b/src/cards/chips-card/chips/conditional-chip-editor.ts index d9016546c..9cc5cfaaa 100644 --- a/src/cards/chips-card/chips/conditional-chip-editor.ts +++ b/src/cards/chips-card/chips/conditional-chip-editor.ts @@ -106,14 +106,13 @@ export class ConditionalChipEditor extends LitElement implements LovelaceChipEdi naturalMenuWidth > ${CHIP_LIST.map( - (chip) => - html` - - ${customLocalize( - `editor.chip.chip-picker.types.${chip}` - )} - - ` + (chip) => html` + + ${customLocalize( + `editor.chip.chip-picker.types.${chip}` + )} + + ` )} `} diff --git a/src/cards/chips-card/chips/conditional-chip.ts b/src/cards/chips-card/chips/conditional-chip.ts index 847564328..6ad9f9a6e 100644 --- a/src/cards/chips-card/chips/conditional-chip.ts +++ b/src/cards/chips-card/chips/conditional-chip.ts @@ -28,7 +28,7 @@ export const setupConditionChipComponent = async () => { }); } const HuiConditionalBase = await loadCustomElement("hui-conditional-base"); - + // @ts-ignore class ConditionalChip extends HuiConditionalBase implements LovelaceChip { public static async getConfigElement(): Promise { diff --git a/src/cards/climate-card/climate-card.ts b/src/cards/climate-card/climate-card.ts index 11777cc1f..8b9ef7498 100644 --- a/src/cards/climate-card/climate-card.ts +++ b/src/cards/climate-card/climate-card.ts @@ -51,7 +51,10 @@ registerCustomCard({ }); @customElement(CLIMATE_CARD_NAME) -export class ClimateCard extends MushroomBaseCard implements LovelaceCard { +export class ClimateCard + extends MushroomBaseCard + implements LovelaceCard +{ public static async getConfigElement(): Promise { await import("./climate-card-editor"); return document.createElement(CLIMATE_CARD_EDITOR_NAME) as LovelaceCardEditor; @@ -70,7 +73,7 @@ export class ClimateCard extends MushroomBaseCard `; diff --git a/src/cards/lock-card/controls/lock-buttons-control.ts b/src/cards/lock-card/controls/lock-buttons-control.ts index d21bab6c6..708a9f0d7 100644 --- a/src/cards/lock-card/controls/lock-buttons-control.ts +++ b/src/cards/lock-card/controls/lock-buttons-control.ts @@ -54,7 +54,7 @@ export class LockButtonsControl extends LitElement { @property({ attribute: false }) public entity!: LockEntity; - @property({ type: Boolean }) public fill: boolean = false; + @property({ type: Boolean }) public fill: boolean = false; private callService(e: CustomEvent) { e.stopPropagation(); diff --git a/src/cards/media-player-card/controls/media-player-media-control.ts b/src/cards/media-player-card/controls/media-player-media-control.ts index 5466c7433..8e8b3a5f9 100644 --- a/src/cards/media-player-card/controls/media-player-media-control.ts +++ b/src/cards/media-player-card/controls/media-player-media-control.ts @@ -17,7 +17,7 @@ export class MediaPlayerMediaControls extends LitElement { @property({ attribute: false }) public controls!: MediaPlayerMediaControl[]; - @property({ type: Boolean }) public fill: boolean = false; + @property({ type: Boolean }) public fill: boolean = false; private _handleClick(e: MouseEvent): void { e.stopPropagation(); diff --git a/src/cards/media-player-card/controls/media-player-volume-control.ts b/src/cards/media-player-card/controls/media-player-volume-control.ts index 9a047baaa..5c9f4ca24 100644 --- a/src/cards/media-player-card/controls/media-player-volume-control.ts +++ b/src/cards/media-player-card/controls/media-player-volume-control.ts @@ -31,7 +31,7 @@ export class MediaPlayerVolumeControls extends LitElement { @property({ attribute: false }) public entity!: MediaPlayerEntity; - @property({ type: Boolean }) public fill: boolean = false; + @property({ type: Boolean }) public fill: boolean = false; @property({ attribute: false }) public controls!: MediaPlayerVolumeControl[]; diff --git a/src/cards/media-player-card/media-player-card.ts b/src/cards/media-player-card/media-player-card.ts index 070e638d9..6e92db76f 100644 --- a/src/cards/media-player-card/media-player-card.ts +++ b/src/cards/media-player-card/media-player-card.ts @@ -151,7 +151,7 @@ export class MediaPlayerCard if (!this._config || !this.hass || !this._config.entity) { return nothing; } - + const stateObj = this._stateObj; if (!stateObj) { diff --git a/src/cards/number-card/number-card.ts b/src/cards/number-card/number-card.ts index e7f005e22..588887f65 100644 --- a/src/cards/number-card/number-card.ts +++ b/src/cards/number-card/number-card.ts @@ -82,7 +82,7 @@ export class NumberCard extends MushroomBaseCard implements Lo updateValue() { this.value = undefined; - const stateObj =this._stateObj; + const stateObj = this._stateObj; if (!stateObj || Number.isNaN(stateObj.state)) return; this.value = Number(stateObj.state); diff --git a/src/cards/vacuum-card/vacuum-card.ts b/src/cards/vacuum-card/vacuum-card.ts index 1869c83d3..0cc47b07a 100644 --- a/src/cards/vacuum-card/vacuum-card.ts +++ b/src/cards/vacuum-card/vacuum-card.ts @@ -72,7 +72,7 @@ export class VacuumCard if (!this._config || !this.hass) { return nothing; } - + const stateObj = this._stateObj; if (!stateObj) { diff --git a/src/shared/editor/layout-picker.ts b/src/shared/editor/layout-picker.ts index 6d5a24f81..803daa8bd 100644 --- a/src/shared/editor/layout-picker.ts +++ b/src/shared/editor/layout-picker.ts @@ -54,13 +54,12 @@ export class LayoutPicker extends LitElement { > ${LAYOUTS.map( - (layout) => - html` - - ${customLocalize(`editor.form.layout_picker.values.${layout}`)} - - - ` + (layout) => html` + + ${customLocalize(`editor.form.layout_picker.values.${layout}`)} + + + ` )} `; From 2785da6162e6f292d4e4062920e8a28c601ca6e1 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 22:12:16 +0100 Subject: [PATCH 21/25] Fix missing condition --- src/cards/vacuum-card/vacuum-card.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cards/vacuum-card/vacuum-card.ts b/src/cards/vacuum-card/vacuum-card.ts index 0cc47b07a..b2c21c4ce 100644 --- a/src/cards/vacuum-card/vacuum-card.ts +++ b/src/cards/vacuum-card/vacuum-card.ts @@ -69,7 +69,7 @@ export class VacuumCard } protected render() { - if (!this._config || !this.hass) { + if (!this._config || !this.hass || !this._config.entity) { return nothing; } From 1f87fce71f8b979fa4e5cb86189ed05d163900e5 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 22:14:28 +0100 Subject: [PATCH 22/25] Fix missing condition --- src/cards/update-card/update-card.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cards/update-card/update-card.ts b/src/cards/update-card/update-card.ts index 9e300a09c..fe98c3cd1 100644 --- a/src/cards/update-card/update-card.ts +++ b/src/cards/update-card/update-card.ts @@ -71,7 +71,7 @@ export class UpdateCard } protected render() { - if (!this._config || !this.hass) { + if (!this._config || !this.hass || !this._config.entity) { return nothing; } From e9e45ad1934bdc1787521edc736aaccd5c498a95 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 22:18:22 +0100 Subject: [PATCH 23/25] Improve get card size --- src/utils/base-card.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/utils/base-card.ts b/src/utils/base-card.ts index 806d82f14..982a8534a 100644 --- a/src/utils/base-card.ts +++ b/src/utils/base-card.ts @@ -42,10 +42,6 @@ export class MushroomBaseCard< return false; } - public getCardSize(): number | Promise { - return 1; - } - setConfig(config: T): void { this._config = { tap_action: { @@ -80,6 +76,23 @@ export class MushroomBaseCard< return [column, row]; } + public getCardSize(): number | Promise { + let height = 1; + if (!this._config) return height; + const appearance = computeAppearance(this._config); + if (appearance.layout === "vertical") { + height += 1; + } + if ( + appearance?.layout !== "horizontal" && + this.hasControls && + !("collapsible_controls" in this._config && this._config?.collapsible_controls) + ) { + height += 1; + } + return height; + } + protected renderPicture(picture: string): TemplateResult { return html` Date: Tue, 5 Mar 2024 22:21:00 +0100 Subject: [PATCH 24/25] fix light card action --- src/cards/light-card/light-card.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cards/light-card/light-card.ts b/src/cards/light-card/light-card.ts index 1e40533c3..89b270ae0 100644 --- a/src/cards/light-card/light-card.ts +++ b/src/cards/light-card/light-card.ts @@ -107,6 +107,9 @@ export class LightCard tap_action: { action: "toggle", }, + hold_action: { + action: "more-info", + }, ...config, }); this.updateActiveControl(); From 91851df3f35d4df8685d62ec6c34243cf48265ce Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Mar 2024 22:21:56 +0100 Subject: [PATCH 25/25] Improve get card size --- src/cards/template-card/template-card.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cards/template-card/template-card.ts b/src/cards/template-card/template-card.ts index dacdefb9c..c5274a0f6 100644 --- a/src/cards/template-card/template-card.ts +++ b/src/cards/template-card/template-card.ts @@ -73,7 +73,13 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard { protected _inGrid = false; public getCardSize(): number | Promise { - return 1; + let height = 1; + if (!this._config) return height; + const appearance = computeAppearance(this._config); + if (appearance.layout === "vertical") { + height += 1; + } + return height; } public getGridSize(): [number, number] {