UNPKG

lovelace-timer-bar-card

Version:

Progress bar display for Home Assistant timers

270 lines (248 loc) 7.88 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import { html, CSSResultGroup, TemplateResult, css, nothing, PropertyValues, } from "lit"; import { property } from "lit/decorators.js"; import { computeRTL, HomeAssistant } from "custom-card-helpers"; import { HassEntity, Mushroom, TimerBarConfig, TimerBarEntityConfig, } from "./types"; import { createActionHandler, createHandleAction } from "./helpers-actions"; import { TimerBarEntityRow, fillConfig } from "./timer-bar-entity-row"; import { defaultColorCss, defaultDarkColorCss, cardStyle, themeColorCss, themeVariables, computeRgbColor, computeInfoDisplay, } from "./lib/mushroom"; const computeObjectId = (entityId: string): string => entityId.substring(entityId.indexOf(".") + 1); const computeStateName = (stateObj: any): string => stateObj.attributes.friendly_name === undefined ? computeObjectId(stateObj.entity_id).replace(/_/g, " ") : stateObj.attributes.friendly_name || ""; const computeDarkMode = (hass: HomeAssistant | undefined) => hass && !!(hass.themes as any).darkMode; /** Style for outer timer bar card */ export function mushroomStyle(mushroom: Mushroom) { if (mushroom.fill_container) return "height: 100%"; return; } export function fillMushroomConfig( config: TimerBarEntityConfig, mushroom: Mushroom, ): TimerBarConfig { let color = "var(--rgb-state-entity)"; if (mushroom.icon_color) color = computeRgbColor(mushroom.icon_color); if (mushroom.color) color = computeRgbColor(mushroom.color); return { ...fillConfig(config), bar_background: `rgba(${color}, 0.2)`, bar_foreground: `rgb(${color})`, bar_radius: "2px", translations: config.translations, ...config, }; } export class TimerBarMushroomRow extends TimerBarEntityRow { @property() public mushroom: Mushroom = {}; @property({ reflect: true, type: String }) public layout: string | undefined; protected updated(changedProps: PropertyValues): void { // This function comes from base-element.ts in mushroom cards super.updated(changedProps); if (changedProps.has("hass") && this.hass) { const currentDarkMode = computeDarkMode(changedProps.get("hass")); const newDarkMode = computeDarkMode(this.hass); if (currentDarkMode !== newDarkMode) { this.toggleAttribute("dark-mode", newDarkMode); } } } protected _renderRow(config: TimerBarConfig, contents: TemplateResult) { if (!this.hass) return html``; const warning = this._warning ? html`<hui-warning>${this._warning}</hui-warning>` : ""; const rtl = computeRTL(this.hass); const state = this.hass!.states[this.config.entity!]; const name = config.name ?? computeStateName(state); if (this.modConfig.layout === "hide_name") config = { ...config, name: "" }; const appearance = this.appearance(); const primary = computeInfoDisplay( appearance.primary_info, name, super._renderState(), state, this.hass, ); return html` <ha-card class=${appearance.fill_container ? "fill-container" : ""} > <mushroom-card ?rtl=${rtl} .appearance=${appearance}> <mushroom-state-item .appearance=${appearance} ?rtl=${rtl} @action=${createHandleAction(this.hass, config)} .actionHandler=${createActionHandler(config)} > ${warning} ${this._renderIcon(state)} ${this._renderBadge(state)} <div class="container" slot="info"> <span class="primary">${primary}</span> <span class="secondary ${this.appearance().layout}" >${contents}</span > </div> </mushroom-state-item> </mushroom-card> ${this._renderDebug()} </ha-card>`; } protected _renderState() { const state = this.hass!.states[this.config.entity!]; const name = this.config.name ?? computeStateName(state); const appearance = this.appearance(); const stateStr = super._renderState(); return computeInfoDisplay( appearance.secondary_info, name, stateStr, state, this.hass!, ) as TemplateResult; } protected _renderIcon(stateObj: HassEntity): TemplateResult { const active = this._mode() == "active"; const icon = active ? this.config.active_icon ?? this.config.icon : this.config.icon; let style = ""; if (this.mushroom.icon_color || this.mushroom.color) { const iconRgbColor = computeRgbColor( this.mushroom.color! || this.mushroom.icon_color!, ); style += `--icon-color:rgb(${iconRgbColor});`; style += `--shape-color:rgba(${iconRgbColor}, 0.2);`; } return html`<mushroom-shape-icon slot="icon" .disabled=${!active} style=${style} > <ha-state-icon .hass=${this.hass} .stateObj=${stateObj} .state=${stateObj} .icon=${icon} ></ha-state-icon> </mushroom-shape-icon>`; } protected _renderBadge(stateObj: HassEntity) { return stateObj.state === "unavailable" ? html`<mushroom-badge-icon class="unavailable" slot="badge" icon="mdi:help" ></mushroom-badge-icon>` : nothing; } private appearance() { // NOTE: Not all of these are used or implemented. return { layout: this.mushroom.layout ?? "default", fill_container: this.mushroom.fill_container ?? false, primary_info: this.mushroom.primary_info ?? "name", secondary_info: this.mushroom.secondary_info ?? "state", icon_type: this.mushroom.icon_type ?? "icon", }; } protected localize(value: string, state: HassEntity, _: boolean) { return super.localize(value, state, false); } static get styles(): CSSResultGroup { return [ super.styles, css` :host { ${defaultColorCss} } :host([dark-mode]) { ${defaultDarkColorCss} } :host { ${themeColorCss} ${themeVariables} } `, cardStyle, css` :host([layout="grid"]) { height: 100%; } mushroom-state-item { cursor: pointer; } mushroom-shape-icon { --icon-color: rgb(var(--rgb-state-entity)); --shape-color: rgba(var(--rgb-state-entity), 0.2); } .container { min-width: 0; flex: 1; display: flex; flex-direction: column; } .primary { font-weight: var(--card-primary-font-weight); font-size: var(--card-primary-font-size); line-height: var(--card-primary-line-height); color: var(--card-primary-color); letter-spacing: var(--card-primary-letter-spacing); text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .secondary { font-weight: var(--card-secondary-font-weight); font-size: var(--card-secondary-font-size); line-height: var(--card-secondary-line-height); color: var(--card-secondary-color); letter-spacing: var(--card-secondary-letter-spacing); text-overflow: ellipsis; overflow: hidden; white-space: nowrap; display: flex; flex-direction: row-reverse; justify-content: start; align-items: center; } .secondary.vertical { flex-direction: column-reverse; justify-content: center; } .secondary.vertical > .text-content { width: 100% !important; text-align: center; } .bar { margin-top: 0; } .bar-container { flex-grow: 1; } .text-content { text-align: start; } `, ]; } }