diff --git a/README.md b/README.md index ca995de..3a179ae 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ All the options are available in the lovelace editor but you can use `yaml` if y | Name | Type | Default | Description | | :------------------ | :-------------------------------------------------- | :---------- | :---------------------------------------------------------------------------------- | -| `entity` | string | Required | Entity | +| `entities` | array of strings | Required | Entities | | `layout` | string | Optional | Layout of the card. Vertical, horizontal and default layout are supported | | `fill_container` | boolean | `false` | Fill container or not. Useful when card is in a grid, vertical or horizontal layout | | `filter_events` | boolean | `false` | Filter fetched events by patterns (if at least one is defined) before selecting the one to display | @@ -119,29 +119,21 @@ All the options are available in the lovelace editor but you can use `yaml` if y | `debug` | boolean | `false` | Option to enable debug mode to help fixing bugs ;) . | | `icon_size` | integer | 40 | Size of the icons in px if you choose `card_style` as `icon` . | | `with_label` | boolean | `true` | Option to decide if you want to see the label in the card or the chip style. | -| `settings` | [Settings](#settings) | Required | Settings to detect the kind of trash and how to display it.| +| `pattern` | array of [Pattern](#pattern) | Required | Pattern to detect the kind of trash and how to display it.| -#### Settings +#### Pattern -| Name | Type | Default | Description | -| :------------------ | :-------------------------------------------------- | :---------- | :---------------------------------------------------------------------------------- | -| `organic` | [TrashTypeConfig](#trash-type-configuration) | Required | Configuration to detect and display that the organic trash is picked up | -| `paper` | [TrashTypeConfig](#trash-type-configuration) | Required | Configuration to detect and display that the paper trash is picked up | -| `recycle` | [TrashTypeConfig](#trash-type-configuration) | Required | Configuration to detect and display that the organic trash is picked up | -| `waste` | [TrashTypeConfig](#trash-type-configuration) | Required | Configuration to detect and display that the waste trash is picked up | -| `others` | [OtherConfig](#other-type-trash-configuration) | Required | Configuration what should be display if non of the others types are matching | - - -#### Trash type configuration | Name | Type | Default | Description | | :------------------ | :-------------------------------------------------- | :---------- | :---------------------------------------------------------------------------------- | +| `type` | `organic`, `paper`, `recycle`, `waste`, `others`, `custom` | Required | Label which should be shown | | `label` | string | Required | Label which should be shown | | `icon` | string | Required | Icon which should be displayed | | `color` | string | Required | Background color of the card which should be used | | `pattern` | string | Required | Pattern used to detected to display the apply this trash type. (Is tested against the calendar entry title) | +| `picture` | string | Optional | picture url to a image to show instead of the icon | #### Other type trash configuration @@ -155,32 +147,50 @@ All the options are available in the lovelace editor but you can use `yaml` if y ```yaml type: custom:trash-card -entity: calendar.mags_abfuhrtermine +entities: + - calendar.mags_abfuhrtermine layout: vertical -settings: - others: - color: purple - icon: mdi:trash-can - organic: - label: Organic +event_grouping: true +drop_todayevents_from: '10:00:00' +next_days: 300 +day_style: counter +card_style: card +color_mode: background +items_per_row: 4 +refresh_rate: 60 +with_label: true +filter_events: false +use_summary: false +hide_time_range: false +pattern: + - label: Organic icon: mdi:flower - color: green - pattern: (braun) - paper: - label: Paper - icon: mdi:newspaper-variant-multiple - color: blue - pattern: (blau) - recycle: - label: Recycle + pattern: braun + color: light-green + type: organic + - label: Paper + icon: mdi:newspaper-variant-multiple-outline + color: indigo + pattern: blau + type: paper + - label: Recycling + pattern: gelb icon: mdi:recycle-variant color: amber - pattern: (gelb) - waste: - label: Trash - icon: mdi:trash-can-outline - color: grey - pattern: (grau) + type: recycle + - pattern: grau + icon: mdi:trash-can + label: Waste + color: dark-grey + type: waste + - icon: mdi:dump-truck + color: purple + type: others + - label: Electric + icon: mdi:electron-framework + color: pink + type: custom + pattern: elektro ``` diff --git a/src/cards/trash-card/container/cards.ts b/src/cards/trash-card/container/cards.ts index 03a913c..d2128a2 100644 --- a/src/cards/trash-card/container/cards.ts +++ b/src/cards/trash-card/container/cards.ts @@ -7,7 +7,6 @@ import '../items/card'; import type { BaseContainerElement } from './BaseContainerElement'; import type { TrashCardConfig } from '../trash-card-config'; -import type { HassEntity } from 'home-assistant-js-websocket'; import type { CalendarItem } from '../../../utils/calendarItem'; import type { HomeAssistant } from '../../../utils/ha'; @@ -32,14 +31,11 @@ class Cards extends LitElement implements BaseContainerElement { } public 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 HassEntity | undefined; - - if (!stateObj || !this.items || this.items.length === 0) { + if (!this.items || this.items.length === 0) { return nothing; } diff --git a/src/cards/trash-card/container/chips.ts b/src/cards/trash-card/container/chips.ts index c6347bf..a723830 100644 --- a/src/cards/trash-card/container/chips.ts +++ b/src/cards/trash-card/container/chips.ts @@ -8,7 +8,6 @@ import '../items/chip'; import type { BaseContainerElement } from './BaseContainerElement'; import type { HomeAssistant } from '../../../utils/ha'; import type { TrashCardConfig } from '../trash-card-config'; -import type { HassEntity } from 'home-assistant-js-websocket'; import type { CalendarItem } from '../../../utils/calendarItem'; @customElement(`${TRASH_CARD_NAME}-chips-container`) @@ -32,14 +31,11 @@ class Chips extends LitElement implements BaseContainerElement { } public 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 HassEntity | undefined; - - if (!stateObj || !this.items || this.items.length === 0) { + if (!this.items || this.items.length === 0) { return nothing; } diff --git a/src/cards/trash-card/container/icons.ts b/src/cards/trash-card/container/icons.ts index 150353a..6384bb2 100644 --- a/src/cards/trash-card/container/icons.ts +++ b/src/cards/trash-card/container/icons.ts @@ -7,7 +7,6 @@ import '../items/icon'; import type { BaseContainerElement } from './BaseContainerElement'; import type { TrashCardConfig } from '../trash-card-config'; -import type { HassEntity } from 'home-assistant-js-websocket'; import type { CalendarItem } from '../../../utils/calendarItem'; import type { HomeAssistant } from '../../../utils/ha'; @@ -32,14 +31,11 @@ class Icons extends LitElement implements BaseContainerElement { } public 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 HassEntity | undefined; - - if (!stateObj || !this.items || this.items.length === 0) { + if (!this.items || this.items.length === 0) { return nothing; } diff --git a/src/cards/trash-card/formSchemas.ts b/src/cards/trash-card/formSchemas.ts index b9d4aed..25c76d8 100644 --- a/src/cards/trash-card/formSchemas.ts +++ b/src/cards/trash-card/formSchemas.ts @@ -234,7 +234,15 @@ const getSchema = (customLocalize: ReturnType, curre ]; const schema: HaFormSchema[] = [ - { name: 'entity', selector: { entity: { domain: 'calendar' }}}, + { + name: 'entities', + selector: { + entity: { + domain: 'calendar', + multiple: true + } + } + }, { type: 'expandable', name: '', diff --git a/src/cards/trash-card/trash-card-config.ts b/src/cards/trash-card/trash-card-config.ts index b9b7307..591378d 100644 --- a/src/cards/trash-card/trash-card-config.ts +++ b/src/cards/trash-card/trash-card-config.ts @@ -1,7 +1,6 @@ import { array, assign, boolean, integer, literal, object, optional, string, union } from 'superstruct'; import { defaultConfigStruct } from '../../utils/form/defaultConfigStruct'; -import type { EntitySharedConfig } from 'lovelace-mushroom/src/shared/config/entity-config'; import type { ItemSettings } from '../../utils/itemSettings'; import type { LovelaceCardConfig } from 'lovelace-mushroom/src/ha'; @@ -21,10 +20,8 @@ const COLORMODES = [ 'icon' ] as const; -type EntityWithOutIcon = Omit; - - type TrashCardConfig = LovelaceCardConfig & - EntityWithOutIcon & { + type TrashCardConfig = LovelaceCardConfig & { + entities: string[]; pattern?: ItemSettings[]; next_days?: number; items_per_row?: number; @@ -48,7 +45,7 @@ type EntityWithOutIcon = Omit; const entityCardConfigStruct = assign( defaultConfigStruct, object({ - entity: optional(string()), + entities: optional(array(string())), name: optional(string()), layout: optional(union([ literal('horizontal'), literal('vertical'), literal('default') ])), fill_container: optional(boolean()), diff --git a/src/cards/trash-card/trash-card.ts b/src/cards/trash-card/trash-card.ts index d1553f8..17a6cc6 100644 --- a/src/cards/trash-card/trash-card.ts +++ b/src/cards/trash-card/trash-card.ts @@ -51,7 +51,7 @@ export class TrashCard extends LitElement { return { type: `custom:${TRASH_CARD_NAME}`, - entity: entities[0] + entities: [ entities[0] ] }; } @@ -113,7 +113,7 @@ export class TrashCard extends LitElement { // eslint-disable-next-line @typescript-eslint/no-floating-promises getCalendarData( this.hass, - this.config.entity!, + this.config.entities, { start, end, dropAfter }, this.debugger, this.config, diff --git a/src/cards/trash-card/utils/migration.ts b/src/cards/trash-card/utils/migration.ts index c17debf..d1b5d8b 100644 --- a/src/cards/trash-card/utils/migration.ts +++ b/src/cards/trash-card/utils/migration.ts @@ -9,11 +9,23 @@ type OldConfigWithSetting = TrashCardConfig & Partial; type LegacayConfig = TrashCardConfig & SettingsOfConfig; const needsConfigToMigrate = (config: Partial): config is LegacayConfig => - 'settings' in config; + 'settings' in config || 'entity' in config; const migrateConfig = (config: LegacayConfig) => { const pattern: ItemSettings[] = []; - const { settings, ...restOfConfiguration } = config; + const { settings, entity, ...restOfConfiguration } = config; + + const newConfiguration = { + ...restOfConfiguration + }; + + if ('entity' in config) { + newConfiguration.entities = Array.isArray(entity) ? entity : [ entity ]; + } + + if (!('settings' in config)) { + return newConfiguration; + } Object.entries(settings).forEach(([ type, data ]) => { pattern.push({ @@ -23,7 +35,7 @@ const migrateConfig = (config: LegacayConfig) => { }); return { - ...restOfConfiguration, + ...newConfiguration, pattern }; }; diff --git a/src/utils/getCalendarData.ts b/src/utils/getCalendarData.ts index d962677..bd9b14d 100644 --- a/src/utils/getCalendarData.ts +++ b/src/utils/getCalendarData.ts @@ -8,17 +8,33 @@ import type { HomeAssistant } from './ha'; import type { RawCalendarEvent } from './calendarEvents'; import type { TrashCardConfig } from '../cards/trash-card/trash-card-config'; -const getCalendarData = async ( +const fetchData = async ( hass: HomeAssistant, calendar: string, + { start, end }: { start: string; end: string } +) => { + const uri = `calendars/${calendar}?start=${start}&end=${end}`; + + return await hass.callApi('GET', uri). + then(data => data.map(item => ({ + ...item, + entity: calendar + }))); +}; + +const getCalendarData = async ( + hass: HomeAssistant, + calendars: string[], { start, end, dropAfter }: { start: string; end: string; dropAfter: boolean }, debuggerInstance: Debugger, config: TrashCardConfig, timezoneOffset: string ) => { - const uri = `calendars/${calendar}?start=${start}&end=${end}`; + const rawCalendarEvents: RawCalendarEvent[] = []; - const rawCalendarEvents = await hass.callApi('GET', uri); + for await (const calendar of calendars) { + rawCalendarEvents.push(...await fetchData(hass, calendar, { start, end })); + } debuggerInstance.reset(); debuggerInstance.log(`timezone`, timezoneOffset); @@ -26,6 +42,8 @@ const getCalendarData = async ( const normalisedEvents = normaliseEvents(rawCalendarEvents, timezoneOffset); + normalisedEvents.sort((evtA, evtB) => evtA.date.start.getTime() - evtB.date.start.getTime()); + const now = new Date(); debuggerInstance.log(`normaliseEvents`, normalisedEvents);