From 937eadcb7ccdbb6970ca4772b4da583ce4274152 Mon Sep 17 00:00:00 2001 From: Ryan Adolf Date: Mon, 20 Dec 2021 14:40:02 -0800 Subject: [PATCH] v1.15: Fix layout on new HA: use local hui-generic-entity-row --- README.md | 6 ++- package.json | 2 +- src/ha-action-handler-directive.ts | 58 ++++++++++++++++++++++ src/ha-generic-entity-row.ts | 79 ++++++++++++++++++++++++++++++ src/timer-bar-card.ts | 4 +- src/timer-bar-entity-row.ts | 19 ++++--- src/types.ts | 4 ++ 7 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 src/ha-action-handler-directive.ts create mode 100644 src/ha-generic-entity-row.ts diff --git a/README.md b/README.md index c79ba6e..618d6af 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,9 @@ Either `entity` or `entities` must be supplied. Use `entity` if you'd like to em | Name | Type | Requirement | Description | Default | |----------------|---------|--------------|------------------------------------------------------------------------------------------------------------|-------------------| -| icon | string | **Optional** | Customize the icon to shown next to the timer | - | +| icon | string | **Optional** | Customize the icon to show next to the timer | - | +| image | string | **Optional** | Customize the image url to show in place of the icon | - | +| state_color | boolean | **Optional** | Change the icon's color if the timer is active | - | | active_icon | boolean | **Optional** | Override `icon` when timer is active | - | | text_width | string | **Optional** | Space alotted for the time remaining (i.e. right offset of bar) | `3.5em` | | bar_width | boolean | **Optional** | Width of progress bar (decrease if the entity name is cut off) | `calc(70% - 7em)` | @@ -95,6 +97,8 @@ Either `entity` or `entities` must be supplied. Use `entity` if you'd like to em † the primary color is taken from your theme using `var(--mdc-theme-primary, #6200ee);` +You can also use [actions](https://www.home-assistant.io/lovelace/actions/) with this card. + ### Card options diff --git a/package.json b/package.json index f4d2537..9e80216 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lovelace-timer-bar-card", - "version": "1.14.0", + "version": "1.15.0", "description": "Progress bar display for Home Assistant timers", "keywords": [ "home-assistant", diff --git a/src/ha-action-handler-directive.ts b/src/ha-action-handler-directive.ts new file mode 100644 index 0000000..d2c80a3 --- /dev/null +++ b/src/ha-action-handler-directive.ts @@ -0,0 +1,58 @@ +/* Minimum code from Home Assistant to create the action handler used in hui-generic-entity-row. */ + +import { noChange } from "lit"; +import { + AttributePart, + directive, + Directive, + DirectiveParameters, +} from "lit/directive.js"; + +interface ActionHandler extends HTMLElement { + holdTime: number; + bind(element: Element, options?: ActionHandlerOptions): void; +} +interface ActionHandlerElement extends HTMLElement { + actionHandler?: { + options: ActionHandlerOptions; + start?: (ev: Event) => void; + end?: (ev: Event) => void; + handleEnter?: (ev: KeyboardEvent) => void; + }; +} + +type ActionHandlerOptions = any; + +const getActionHandler = (): ActionHandler => { + const body = document.body; + if (body.querySelector("action-handler")) { + return body.querySelector("action-handler") as ActionHandler; + } + + const actionhandler = document.createElement("action-handler"); + body.appendChild(actionhandler); + + return actionhandler as ActionHandler; +}; + +export const actionHandlerBind = ( + element: ActionHandlerElement, + options?: ActionHandlerOptions +) => { + const actionhandler: ActionHandler = getActionHandler(); + if (!actionhandler) { + return; + } + actionhandler.bind(element, options); +}; + +export const actionHandler = directive( + class extends Directive { + update(part: AttributePart, [options]: DirectiveParameters) { + actionHandlerBind(part.element as ActionHandlerElement, options); + return noChange; + } + + render(_options?: ActionHandlerOptions) {} + } +); diff --git a/src/ha-generic-entity-row.ts b/src/ha-generic-entity-row.ts new file mode 100644 index 0000000..eb1fccc --- /dev/null +++ b/src/ha-generic-entity-row.ts @@ -0,0 +1,79 @@ +/* Simplified version of the hui-generic-entity-row. + * This element acts as a stable base to create the timer entity row. + */ + +import { css, html, TemplateResult } from "lit"; +import { ActionHandlerEvent, HomeAssistant, handleAction, hasAction } from "custom-card-helpers"; +import { actionHandler } from "./ha-action-handler-directive"; +import { TimerBarEntityConfig } from "./types"; + +const computeObjectId = (entityId: string): string => + entityId.substr(entityId.indexOf(".") + 1); + +const computeStateName = (stateObj: any): string => + stateObj.attributes.friendly_name === undefined + ? computeObjectId(stateObj.entity_id).replace(/_/g, " ") + : stateObj.attributes.friendly_name || ""; + +export function genericEntityRow(children: TemplateResult, hass?: HomeAssistant, config?: TimerBarEntityConfig): TemplateResult { + if (!hass || !config) return html``; + const stateObj = config.entity ? hass.states[config.entity] : undefined; + if (!stateObj) return html`Entity ${config.entity} not found`; + + const name = config.name || computeStateName(stateObj); + + const _handleAction = (ev: ActionHandlerEvent) => { + handleAction(ev.target as any, hass!, config!, ev.detail.action!); + } + + return html`
+ +
+ ${name} +
+ ${children} +
`; +} + +export const genericEntityRowStyles = css` + .generic-entity-row { + display: flex; + align-items: center; + flex-direction: row; + } + .info { + margin-left: 16px; + margin-right: 8px; + flex: 1 1 30%; + } + .info, + .info > * { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + state-badge { + flex: 0 0 40px; + } +`; diff --git a/src/timer-bar-card.ts b/src/timer-bar-card.ts index c70c59c..e851deb 100644 --- a/src/timer-bar-card.ts +++ b/src/timer-bar-card.ts @@ -21,8 +21,8 @@ import { version } from '../package.json'; window.customElements.define('timer-bar-entity-row', TimerBarEntityRow); console.info( `%c TIMER-BAR-CARD %c Version ${version} `, - 'font-weight: bold; background: #aeb', - 'font-weight: bold; background: #ddd', + 'font-weight: bold; color: #000; background: #aeb', + 'font-weight: bold; color: #000; background: #ddd', ); @customElement('timer-bar-card') diff --git a/src/timer-bar-entity-row.ts b/src/timer-bar-entity-row.ts index 6d2c626..8e0a3ca 100644 --- a/src/timer-bar-entity-row.ts +++ b/src/timer-bar-entity-row.ts @@ -6,6 +6,7 @@ import { styleMap } from 'lit/directives/style-map.js'; import { HomeAssistant, hasConfigOrEntityChanged, secondsToDuration, computeStateDisplay } from 'custom-card-helpers'; import { findDuration, formatStartTime, timerTimeRemaining, timerTimePercent, findMode, stateMode, autoMode, tryDurationToSeconds } from './helpers'; import { TimerBarEntityConfig, HassEntity, Translations, TimerBarConfig, Mode } from './types'; +import { genericEntityRow, genericEntityRowStyles } from './ha-generic-entity-row'; export function fillConfig(config: TimerBarEntityConfig) { return { @@ -111,7 +112,7 @@ export class TimerBarEntityRow extends LitElement { case 'pause': return this._renderRow(activeConfig, html` -
+
${localize(this.hass!, state.state, state, this.config.translations)}
@@ -121,7 +122,7 @@ export class TimerBarEntityRow extends LitElement { case 'waiting': return this._renderRow(this.modConfig, html` -
+
${localize(this.hass!, "scheduled_for", undefined, this.config.translations)} ${formatStartTime(state)}
`); @@ -138,9 +139,7 @@ export class TimerBarEntityRow extends LitElement { private _renderRow(config: TimerBarConfig, contents: TemplateResult) { if (this.modConfig.full_row) return html`
${contents}
${this._renderDebug()}`; return html` - - ${contents} - + ${genericEntityRow(contents, this.hass, config)} ${this._renderDebug()} `; } @@ -154,7 +153,7 @@ export class TimerBarEntityRow extends LitElement { const containerStyle = styleMap({ width: this._bar_width, direction: this.modConfig.bar_direction }); const bgStyle = this._barStyle('100%', this.modConfig.bar_background!); const fgStyle = this._barStyle(percent+"%", this.modConfig.bar_foreground!); - return html`
+ return html`
@@ -256,22 +255,22 @@ export class TimerBarEntityRow extends LitElement { } static get styles(): CSSResultGroup { - return css` + return [css` :host { display: flex; flex-direction: column; justify-content: center; } + .pointer { cursor: pointer; } .flex { display: flex; height: 40px; align-items: center; justify-content: flex-end; } .bar-container { - cursor: pointer; min-height: 1.5em; display: flex; flex-shrink: 0; align-items: center; } .bar { margin-top: 2px; } - .status { cursor: pointer; line-height: 1.5em; flex-shrink: 0; } + .status { line-height: 1.5em; flex-shrink: 0; } .text-content { text-align: right; text-align: end; overflow: hidden; } code { display: block; @@ -281,7 +280,7 @@ export class TimerBarEntityRow extends LitElement { font-size: 0.9em; word-break: break-all; } - `; + `, genericEntityRowStyles]; } private get modConfig(): TimerBarEntityConfig { diff --git a/src/types.ts b/src/types.ts index 428c768..1ffb8b6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,6 +19,8 @@ export declare type HassEntity = { interface styleConfig { icon?: string; active_icon?: string; + image?: string; + state_color?: boolean; bar_width?: string; bar_height?: string; @@ -60,6 +62,8 @@ export interface TimerBarEntityConfig extends styleConfig { modifications?: modsConfig[]; translations?: Translations; + hold_action?: any; + double_tap_action?: any; } export interface TimerBarConfig extends TimerBarEntityConfig {