diff --git a/CHANGELOG.md b/CHANGELOG.md index a37f021..5d2888f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,8 @@ The format is based on [Keep a Changelog][keep-a-changelog] ## [Unreleased] ## What's Changed - * Card now supports setting multiple `entities` in the card configuration. This allows you to show multiple departure boards in one card. + * Previous configurations are still supported, but will be migrated automatically as soon as you open the card editor and save the configuration. * Editor bundle name changed to `hasl-departure-card-editor.js` to help with manual installation. ## [3.2.0] (2025-01-17) diff --git a/readme.md b/readme.md index 09c0a2e..1bd0996 100644 --- a/readme.md +++ b/readme.md @@ -39,10 +39,9 @@ Card fully supports configuration through the UI ### Options | Name | Type | Required? | Description | |----------------------|------------------|-----------|-------------------------------------------------------------------------------------------------------------| -| entity | entity | required | The entity_id of the 'Departure' sensor. | -| entities | entity[] | required | The array of entity_id of the 'Departure' sensors. If set, takes precedence over `entity` | +| entities | entity[] | required | The array of entity_id's of the 'Departure' sensors. | | title | string | optional | If set, this will be rendered as the card name. | -| show_entity_name | bool | optional | Render a friendly name of a selected `entity`. Disabled when `entities` are set | +| show_entity_name | bool | optional | Render a friendly name of a selected `entity`. Disabled when `entities` are set to more than 1 entity | | show_header | bool | optional | Render headers in each section such as "Line", "Destination" and "Departure". | | show_icon | bool | optional | Render transport icon for each line. | | show_departures | bool | optional | Render departures section. | @@ -51,12 +50,11 @@ Card fully supports configuration through the UI | hide_departed | bool | optional | If set, will hide already departured vehicles. | | show_departed_offset | bool | optional | If set, will show some departed vehicles, which departed less than the offset minutes ago. | | show_time_always | bool | optional | Always present time in HH:MM form. If not set, time will be presented as "in X minutes" or "X minutes ago". | -| show_updated | bool | optional | Render the 'last updated' text. Disabled when `entities` are set | +| show_updated | bool | optional | Render the 'last updated' text. Disabled when `entities` are set to more than 1 entity | | language | string | optional | The texts will be rendered in this language. Can be one of `sv-SE`, `en-EN`, `fr-FR`. | | click_action | string or object | optional | Action when tapping the card. See section `click_action` below. | -Only one of `entity` and `entities` is required to function. If both are specified, `entities` takes precedence and `entity` is ignored. -Setting `entities` will render departures from all the sensors as one list, that is sorted by `expected` time. +Setting `entities` to more than one entity will render departures from all of the sensors as one list sorted by `expected` time but will disable `show_entity_name` and `show_updated` options. #### `click_action` diff --git a/src/DepartureCard/DepartureCard.config.ts b/src/DepartureCard/DepartureCard.config.ts index 7ca21f9..5cb2a75 100644 --- a/src/DepartureCard/DepartureCard.config.ts +++ b/src/DepartureCard/DepartureCard.config.ts @@ -14,8 +14,8 @@ export type ClickAction = 'info' | EntityInfoAction | ServiceCallAction export interface DepartureCardConfig extends LovelaceCardConfig { title?: string - entity: string - entities?: string[] + entity?: string + entities: string[] show_entity_name?: boolean show_header?: boolean @@ -34,9 +34,8 @@ export interface DepartureCardConfig extends LovelaceCardConfig { } export const DEFAULT_CONFIG: Partial = { - entity: '', title: '', - entities: undefined, + entities: [], show_entity_name: true, show_header: true, diff --git a/src/DepartureCard/index.ts b/src/DepartureCard/index.ts index b6a6fe4..a5ac9d3 100644 --- a/src/DepartureCard/index.ts +++ b/src/DepartureCard/index.ts @@ -29,14 +29,20 @@ export class HASLDepartureCard extends LitElement implements LovelaceCard { } getCardSize() { + const singleEntitityExtras = (this.isManyEntitiesSet() + ? () => 0 + : () => { + const [_, attrs] = this.getFirstEntity() + if (!attrs) return 0 + + return (this.config.show_entity_name && attrs.friendly_name) ? 1 : 0 + })() + const deps = this.getDepartures(); - const entity = this.config?.entity; - const data = this.hass?.states[entity] - const attrs = data.attributes const size = [ !!this.config.title ? 1 : 0, - ((this.config.show_entity_name && attrs.friendly_name)) ? 1 : 0, + singleEntitityExtras, !!this.config.show_header ? 1 : 0, deps?.length || 0, ].reduce((sum, entity) => sum += entity ? entity : 0, 0); @@ -46,8 +52,8 @@ export class HASLDepartureCard extends LitElement implements LovelaceCard { getLayoutOptions() { return { - grid_min_columns: 3, - grid_min_rows: 2, + grid_min_columns: 3, + grid_min_rows: 2, }; } @@ -77,10 +83,8 @@ export class HASLDepartureCard extends LitElement implements LovelaceCard { this.isManyEntitiesSet() ? () => nothing : () => { - const entity = this.config?.entity - if (!entity) return nothing; - - const data = this.hass?.states[entity] + const [data, __] = this.getFirstEntity() + if (!data) return nothing; return (this.config?.show_updated && data.last_updated) ? html` @@ -104,20 +108,23 @@ export class HASLDepartureCard extends LitElement implements LovelaceCard { ` } - private isManyEntitiesSet(): boolean { - return this.config?.entities?.length > 0 - } + private isManyEntitiesSet = () => this.config?.entities?.length > 1 - private getDeparturesFor(entity: string) { - if (entity === undefined) return undefined; + private getFirstEntity(): [HassEntity, DepartureAttributes] | [undefined, undefined] { + const data = this.hass?.states[this.config?.entities?.[0] || this.config?.entity]; + const attrs = data?.attributes + if (data && attrs && isDepartureAttrs(attrs)) { + return [data, attrs] + } + return [undefined, undefined] + } - const data = this.hass?.states[entity] - if (data === undefined) return undefined; - if (!isDepartureAttrs(data.attributes)) return undefined; + private getDeparturesFor(attrs: DepartureAttributes | undefined) { + if (!attrs) return [] const now = new Date() - return (data.attributes.departures + return (attrs.departures // filter direction ?.filter((d) => { if (this.config?.direction === 0) return true @@ -173,22 +180,19 @@ export class HASLDepartureCard extends LitElement implements LovelaceCard { private getDepartures() { if (this.isManyEntitiesSet()) { - return this.getDeparturesCombined(this.config?.entities) - } else { - return this.getDeparturesFor(this.config?.entity) + return this.getDeparturesCombined(this.config?.entities || []) } + + const [_, attrs] = this.getFirstEntity() + if (!attrs) return undefined; + + return this.getDeparturesFor(attrs) } private renderDepartures = () => { const renderEntityName = () => { - const entity = this.config?.entity - if (entity === undefined) return nothing; - - const data = this.hass?.states[this.config?.entity] - if (data === undefined) return nothing; - - const attrs = data.attributes - if (!isDepartureAttrs(attrs)) return nothing; + const [_, attrs] = this.getFirstEntity() + if (!attrs) return nothing; return (this.config.show_entity_name && attrs.friendly_name) ? html`
${attrs.friendly_name} string) => { - const haveMultipleEntities = this._config?.entities?.length > 0 + const haveMultipleEntities = this._config?.entities?.length > 1 return [ { name: "title", selector: { text: {} } }, @@ -33,7 +39,6 @@ export class HASLDepartureCardEditor extends LitElement implements LovelaceCardE icon: "mdi:database", title: _("editor_entities"), schema: [ - { name: "entity", disabled: haveMultipleEntities, selector: { entity: { filter: { domain: 'sensor', integration: 'hasl3' }}} }, { name: "show_entity_name", type: "boolean", disabled: haveMultipleEntities }, { name: "entities", selector: { entity: { multiple: true, filter: { domain: 'sensor', integration: 'hasl3' }}} }, ], @@ -76,8 +81,10 @@ export class HASLDepartureCardEditor extends LitElement implements LovelaceCardE private _valueChanged(ev: CustomEvent): void { ev.stopPropagation(); - const newConfig = ev.detail.value + this._dispatchConfigChangedEvent(ev.detail.value) + } + private _dispatchConfigChangedEvent(newConfig) { const event = new Event("config-changed", { bubbles: true, composed: true}); event.detail = { config: newConfig }; this.dispatchEvent(event);