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

Adding configurable backends #1

Merged
merged 3 commits into from
Dec 2, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ The standard configuration consists of the following:
| Name | Type | Default | Description |
| ------------------------ | -------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `type` | string | **Required** | `custom:scheduler-card` |
| `backend_platform` | string | "scheduler" | Which platform to use in the backend, to handle storage of scheduler data.
| `standard_configuration` | boolean | *true* | Use the [standard configuration](#standard-configuration) as a base configuration. |
| `discover_existing` | boolean | *true* | Show previously created schedules in the card, also if the related entities are not included in the configuration.<br>Set to `false` if you have multiple scheduler-cards.<br>See [schedule discovery](#schedule-discovery) for more info. |
| `include` | list | none | List of filters to determine which HA entities are available for creating schedules.<br> See [include](#include) for more info. |
Expand Down
103 changes: 55 additions & 48 deletions dist/standalone-schedule-card.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/config-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export function ValidateConfig(config: any) {
}
};

Optional(config, 'backend_platform', 'string');
Optional(config, 'discover_existing', 'boolean');
Optional(config, 'standard_configuration', 'boolean');
Optional(config, 'title', ['boolean', 'string']);
Expand Down
5 changes: 2 additions & 3 deletions src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export enum EViews {

export const DefaultCardConfig: CardConfig = {
type: "scheduler-card",
backend_platform: "scheduler",
discover_existing: true,
standard_configuration: true,
include: [],
Expand All @@ -40,6 +41,4 @@ export const DefaultCardConfig: CardConfig = {
icon: "action"
},
tags: []
};

export const WebsocketEvent = "scheduler_updated";
};
15 changes: 15 additions & 0 deletions src/data/api_event_types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CardConfig } from "../types";

export const fetchScheduleEventType = (config: CardConfig | null | undefined): string => config?.backend_platform || "scheduler"

export const fetchScheduleItemEventType = (config: CardConfig | null | undefined): string => `${config?.backend_platform || "scheduler"}/item`

export const saveScheduleEventType = (config: CardConfig | null | undefined): string => `${config?.backend_platform || "scheduler"}/add`

export const editScheduleEventType = (config: CardConfig | null | undefined): string => `${config?.backend_platform || "scheduler"}/edit`

export const deleteScheduleEventType = (config: CardConfig | null | undefined): string => `${config?.backend_platform || "scheduler"}/remove`

export const fetchTagsEventType = (config: CardConfig | null | undefined): string => `${config?.backend_platform || "scheduler"}/tags`

export const scheduleUpdatedEventType = (config: CardConfig | null | undefined): string => `${config?.backend_platform || "scheduler"}_updated`
51 changes: 29 additions & 22 deletions src/data/websockets.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
import { HomeAssistant, fireEvent } from "custom-card-helpers";
import { Schedule, ScheduleConfig, TagEntry } from "../types";
import { CardConfig, Schedule, ScheduleConfig, TagEntry } from "../types";
import { fetchScheduleEventType, fetchScheduleItemEventType, saveScheduleEventType, editScheduleEventType, deleteScheduleEventType, fetchTagsEventType } from "./api_event_types";
import { html, TemplateResult } from 'lit';

export const fetchSchedules = (hass: HomeAssistant): Promise<Schedule[]> =>
export const fetchSchedules = (hass: HomeAssistant, config: CardConfig | null | undefined): Promise<Schedule[]> =>
hass.callWS({
type: "scheduler",
type: fetchScheduleEventType(config),
});

export const fetchScheduleItem = (hass: HomeAssistant, schedule_id: string): Promise<Schedule> =>
export const fetchScheduleItem = (hass: HomeAssistant, config: CardConfig | null | undefined, schedule_id: string): Promise<Schedule> =>
hass.callWS({
type: "scheduler/item",
type: fetchScheduleItemEventType(config),
schedule_id: schedule_id
});

export const saveSchedule = (hass: HomeAssistant, config: ScheduleConfig): Promise<boolean> => {
return hass
.callApi("POST", "scheduler/add", config)
};

export const editSchedule = (hass: HomeAssistant, config: ScheduleConfig & { schedule_id: string }): Promise<boolean> => {
return hass
.callApi("POST", "scheduler/edit", config)
};

export const deleteSchedule = (hass: HomeAssistant, schedule_id: string): Promise<boolean> => {
return hass
.callApi("POST", "scheduler/remove", { schedule_id: schedule_id })
};

export const fetchTags = (hass: HomeAssistant): Promise<TagEntry[]> =>
export const saveSchedule = (hass: HomeAssistant, config: CardConfig | null | undefined, schedule_config: ScheduleConfig): Promise<boolean> =>
hass.callApi(
"POST",
saveScheduleEventType(config),
schedule_config
);

export const editSchedule = (hass: HomeAssistant, config: CardConfig | null | undefined, schedule_config: ScheduleConfig & { schedule_id: string }): Promise<boolean> =>
hass.callApi(
"POST",
editScheduleEventType(config),
schedule_config
);

export const deleteSchedule = (hass: HomeAssistant, config: CardConfig | null | undefined, schedule_id: string): Promise<boolean> =>
hass.callApi(
"POST",
deleteScheduleEventType(config),
{ schedule_id: schedule_id }
);

export const fetchTags = (hass: HomeAssistant, config: CardConfig | null | undefined): Promise<TagEntry[]> =>
hass.callWS({
type: "scheduler/tags",
type: fetchTagsEventType(config),
});

export function showErrorDialog(target: HTMLElement, error: string | TemplateResult) {
Expand Down
8 changes: 4 additions & 4 deletions src/scheduler-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,15 +282,15 @@ export class SchedulerCard extends LitElement {

if (this.editItem) {
if (IsDefaultName(schedule.name)) schedule = { ...schedule, name: "" };
editSchedule(this._hass, { ...schedule, schedule_id: this.editItem })
editSchedule(this._hass, this._config, { ...schedule, schedule_id: this.editItem })
.catch(e => handleError(e, this))
.then(() => {
this.editItem = null;
this._view = EViews.Overview;
});
}
else {
saveSchedule(this._hass, schedule)
saveSchedule(this._hass, this._config, schedule)
.catch(e => handleError(e, this))
.then(() => {
this._view = EViews.Overview;
Expand All @@ -301,7 +301,7 @@ export class SchedulerCard extends LitElement {
_deleteItemClick(): void {
if (!this.editItem || !this._hass) return;
const entity_id = this.editItem;
deleteSchedule(this._hass, entity_id)
deleteSchedule(this._hass, this._config, entity_id)
.catch(e => handleError(e, this))
.then(() => {
this.editItem = null;
Expand All @@ -312,7 +312,7 @@ export class SchedulerCard extends LitElement {
async _editItemClick(ev: CustomEvent) {
if (!this._hass || !this._config) return;

const data = await fetchScheduleItem(this._hass, ev.detail);
const data = await fetchScheduleItem(this._hass, this._config, ev.detail);
if (!data) return;
const entities = unique(flatten(data.timeslots.map(e => e.actions.map(e => e.entity_id || e.service))));
this.entities = entities.map(e => parseEntity(e, this._hass!, this._config!));
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ export interface ImportedEntry {
/* config */

export interface CardConfig extends LovelaceCardConfig {
backend_platform: string;
discover_existing: boolean;
standard_configuration: boolean;
title: boolean | string;
Expand Down
24 changes: 22 additions & 2 deletions src/views/scheduler-card-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export class SchedulerCardEditor extends LitElement implements LovelaceCardEdito

async firstUpdated() {
await loadHaForm();
this.scheduleEntities = (await fetchSchedules(this.hass!)).map(e => e.entity_id);
const tagOptions = (await fetchTags(this.hass!)).map(e => e.name);
this.scheduleEntities = (await fetchSchedules(this.hass!, this._config)).map(e => e.entity_id);
const tagOptions = (await fetchTags(this.hass!, this._config)).map(e => e.name);
tagOptions.sort(sortAlphabetically);
this.tagOptions = tagOptions;
}
Expand Down Expand Up @@ -74,6 +74,13 @@ export class SchedulerCardEditor extends LitElement implements LovelaceCardEdito
`
: ''}

<div class="header">Which backend platform to use</div>
<paper-input
label="Platform"
.value=${this.getPlatform()}
@value-changed=${this.updatePlatform}
></paper-input>

<div class="header">Show all schedules</div>
<div class="text-field">
This sets the 'discover existing' parameter.<br />
Expand Down Expand Up @@ -135,6 +142,12 @@ export class SchedulerCardEditor extends LitElement implements LovelaceCardEdito
else return localize('ui.panel.common.title', getLocale(this.hass));
}

private getPlatform() {
if (!this._config || !this.hass) return '';
if (typeof this._config.backend_platform == 'string') return this._config.backend_platform;
else return '';
}

private updateTitleOption(e: Event) {
const type = (e.target as HTMLInputElement).value;
if (!this._config || !this.hass) return;
Expand All @@ -159,6 +172,13 @@ export class SchedulerCardEditor extends LitElement implements LovelaceCardEdito
fireEvent(this, 'config-changed', { config: this._config });
}

private updatePlatform(ev: Event) {
if (!this._config || !this.hass) return;
const value = String((ev.target as HTMLInputElement).value);
this._config = { ...this._config, backend_platform: value };
fireEvent(this, 'config-changed', { config: this._config });
}

getDiscoveryOption() {
if (!this._config || !this.hass) return;
const discover_existing = this._config.hasOwnProperty('discover_existing') ? this._config.discover_existing : true;
Expand Down
8 changes: 4 additions & 4 deletions src/views/scheduler-entities-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import '../components/scheduler-entity-row';
import { capitalize, getLocale, AsArray } from '../helpers';
import { fetchSchedules, fetchScheduleItem } from '../data/websockets';
import { entityFilter } from '../data/entities/entity_filter';
import { WebsocketEvent } from '../const';
import { scheduleUpdatedEventType } from '../data/api_event_types';

const computeScheduleTimestamp = (schedule: Schedule) =>
new Date(schedule.timestamps[schedule.next_entries[0]]).valueOf()
Expand Down Expand Up @@ -65,14 +65,14 @@ export class SchedulerEntitiesCard extends SubscribeMixin(LitElement) {
return [
this.hass!.connection.subscribeMessage(
(ev: SchedulerEventData) => this.updateScheduleItem(ev),
{ type: WebsocketEvent }
{ type: scheduleUpdatedEventType(this.config) }
)
];
}

private async updateScheduleItem(ev: SchedulerEventData): Promise<void> {
//only update single schedule
fetchScheduleItem(this.hass!, ev.schedule_id)
fetchScheduleItem(this.hass!, this.config, ev.schedule_id)
.then(schedule => {
const oldSchedule = this.schedules?.find(e => e.schedule_id == ev.schedule_id);
let schedules = [...this.schedules || []];
Expand Down Expand Up @@ -105,7 +105,7 @@ export class SchedulerEntitiesCard extends SubscribeMixin(LitElement) {
private async loadSchedules(): Promise<void> {
//load all schedules

fetchSchedules(this.hass!)
fetchSchedules(this.hass!, this.config)
.then(res => {
let schedules = res;

Expand Down
2 changes: 1 addition & 1 deletion src/views/scheduler-entitypicker-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class SchedulerEditorCard extends LitElement {
timeSchemeSelected = false;

async firstUpdated() {
this.scheduleEntities = (await fetchSchedules(this.hass!)).map(e => e.entity_id);
this.scheduleEntities = (await fetchSchedules(this.hass!, this.config)).map(e => e.entity_id);
if (this.entities && this.entities.length) {
const group = this.getGroups().find(group => group.entities.find(e => e == this.entities![0].id));
if (!group) return;
Expand Down
2 changes: 1 addition & 1 deletion src/views/scheduler-options-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class SchedulerOptionsCard extends LitElement {
async firstUpdated() {
if (this.config?.tags) {
(async () => await loadHaForm())();
const tagEntries = await fetchTags(this.hass!);
const tagEntries = await fetchTags(this.hass!, this.config);
const existingTags = tagEntries.map(e => e.name);
const configTags = AsArray(this.config.tags);
this.tags = [...existingTags, ...configTags.filter(e => !existingTags.includes(e) && e != 'none')];
Expand Down