Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple calendar entities #242

Merged
merged 3 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 45 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand All @@ -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

Expand All @@ -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
```


Expand Down
8 changes: 2 additions & 6 deletions src/cards/trash-card/container/cards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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;
}

Expand Down
8 changes: 2 additions & 6 deletions src/cards/trash-card/container/chips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand All @@ -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;
}

Expand Down
8 changes: 2 additions & 6 deletions src/cards/trash-card/container/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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;
}

Expand Down
10 changes: 9 additions & 1 deletion src/cards/trash-card/formSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,15 @@ const getSchema = (customLocalize: ReturnType<typeof setupCustomlocalize>, curre
];

const schema: HaFormSchema[] = [
{ name: 'entity', selector: { entity: { domain: 'calendar' }}},
{
name: 'entities',
selector: {
entity: {
domain: 'calendar',
multiple: true
}
}
},
{
type: 'expandable',
name: '',
Expand Down
9 changes: 3 additions & 6 deletions src/cards/trash-card/trash-card-config.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -21,10 +20,8 @@ const COLORMODES = [
'icon'
] as const;

type EntityWithOutIcon = Omit<EntitySharedConfig, 'icon'>;

type TrashCardConfig = LovelaceCardConfig &
EntityWithOutIcon & {
type TrashCardConfig = LovelaceCardConfig & {
entities: string[];
pattern?: ItemSettings[];
next_days?: number;
items_per_row?: number;
Expand All @@ -48,7 +45,7 @@ type EntityWithOutIcon = Omit<EntitySharedConfig, 'icon'>;
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()),
Expand Down
4 changes: 2 additions & 2 deletions src/cards/trash-card/trash-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class TrashCard extends LitElement {

return {
type: `custom:${TRASH_CARD_NAME}`,
entity: entities[0]
entities: [ entities[0] ]
};
}

Expand Down Expand Up @@ -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,
Expand Down
18 changes: 15 additions & 3 deletions src/cards/trash-card/utils/migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,23 @@ type OldConfigWithSetting = TrashCardConfig & Partial<SettingsOfConfig>;
type LegacayConfig = TrashCardConfig & SettingsOfConfig;

const needsConfigToMigrate = (config: Partial<OldConfigWithSetting>): 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({
Expand All @@ -23,7 +35,7 @@ const migrateConfig = (config: LegacayConfig) => {
});

return {
...restOfConfiguration,
...newConfiguration,
pattern
};
};
Expand Down
24 changes: 21 additions & 3 deletions src/utils/getCalendarData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,42 @@ 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<RawCalendarEvent[]>('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<RawCalendarEvent[]>('GET', uri);
for await (const calendar of calendars) {
rawCalendarEvents.push(...await fetchData(hass, calendar, { start, end }));
}

debuggerInstance.reset();
debuggerInstance.log(`timezone`, timezoneOffset);
debuggerInstance.log(`calendar data`, rawCalendarEvents);

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);
Expand Down