Template markdown card (#3451)

* Render templates in markdown card

* Add manual entity_id option

* Linting

* Address review comments

* Address review comments

* Address review comments

* Address review comments

* Tweak disconnect function

* Remove cardSize instance variable

* Fix demo
This commit is contained in:
Thomas Lovén 2019-08-10 21:55:32 +02:00 committed by Paulus Schoutsen
parent c15629b81b
commit 1f3a5b1396
4 changed files with 99 additions and 6 deletions

20
src/data/ws-templates.ts Normal file
View File

@ -0,0 +1,20 @@
import { Connection, UnsubscribeFunc } from "home-assistant-js-websocket";
interface RenderTemplateResult {
result: string;
}
export const subscribeRenderTemplate = (
conn: Connection,
onChange: (result: string) => void,
params: {
template: string;
entity_ids?: string | string[];
variables?: object;
}
): Promise<UnsubscribeFunc> => {
return conn.subscribeMessage(
(msg: RenderTemplateResult) => onChange(msg.result),
{ type: "render_template", ...params }
);
};

View File

@ -24,7 +24,10 @@ export interface MockHomeAssistant extends HomeAssistant {
updateHass(obj: Partial<MockHomeAssistant>); updateHass(obj: Partial<MockHomeAssistant>);
updateStates(newStates: HassEntities); updateStates(newStates: HassEntities);
addEntities(entites: Entity | Entity[], replace?: boolean); addEntities(entites: Entity | Entity[], replace?: boolean);
mockWS(type: string, callback: (msg: any) => any); mockWS(
type: string,
callback: (msg: any, onChange?: (response: any) => void) => any
);
mockAPI(path: string | RegExp, callback: MockRestCallback); mockAPI(path: string | RegExp, callback: MockRestCallback);
mockEvent(event); mockEvent(event);
mockTheme(theme: { [key: string]: string } | null); mockTheme(theme: { [key: string]: string } | null);
@ -108,7 +111,7 @@ export const provideHass = (
console.error(`Unknown WS command: ${msg.type}`); console.error(`Unknown WS command: ${msg.type}`);
} }
}, },
sendMessagePromise: (msg) => { sendMessagePromise: async (msg) => {
const callback = wsCommands[msg.type]; const callback = wsCommands[msg.type];
return callback return callback
? callback(msg) ? callback(msg)
@ -119,6 +122,17 @@ export const provideHass = (
} is not implemented in provide_hass.`, } is not implemented in provide_hass.`,
}); });
}, },
subscribeMessage: async (onChange, msg) => {
const callback = wsCommands[msg.type];
return callback
? callback(msg, onChange)
: Promise.reject({
code: "command_not_mocked",
message: `WS Command ${
msg.type
} is not implemented in provide_hass.`,
});
},
subscribeEvents: async ( subscribeEvents: async (
// @ts-ignore // @ts-ignore
callback, callback,

View File

@ -8,12 +8,15 @@ import {
CSSResult, CSSResult,
} from "lit-element"; } from "lit-element";
import { classMap } from "lit-html/directives/class-map"; import { classMap } from "lit-html/directives/class-map";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-markdown"; import "../../../components/ha-markdown";
import { HomeAssistant } from "../../../types";
import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LovelaceCard, LovelaceCardEditor } from "../types";
import { MarkdownCardConfig } from "./types"; import { MarkdownCardConfig } from "./types";
import { subscribeRenderTemplate } from "../../../data/ws-templates";
@customElement("hui-markdown-card") @customElement("hui-markdown-card")
export class HuiMarkdownCard extends LitElement implements LovelaceCard { export class HuiMarkdownCard extends LitElement implements LovelaceCard {
@ -27,11 +30,16 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
} }
@property() private _config?: MarkdownCardConfig; @property() private _config?: MarkdownCardConfig;
@property() private _content?: string = "";
@property() private _unsubRenderTemplate?: Promise<UnsubscribeFunc>;
@property() private _hass?: HomeAssistant;
public getCardSize(): number { public getCardSize(): number {
return ( return this._config === undefined
this._config!.content.split("\n").length + (this._config!.title ? 1 : 0) ? 3
); : this._config.card_size === undefined
? this._config.content.split("\n").length + (this._config.title ? 1 : 0)
: this._config.card_size;
} }
public setConfig(config: MarkdownCardConfig): void { public setConfig(config: MarkdownCardConfig): void {
@ -40,6 +48,20 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
} }
this._config = config; this._config = config;
this._disconnect();
if (this._hass) {
this._connect();
}
}
public disconnectedCallback() {
this._disconnect();
}
public set hass(hass) {
this._hass = hass;
this._connect();
} }
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
@ -53,12 +75,47 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
class="markdown ${classMap({ class="markdown ${classMap({
"no-header": !this._config.title, "no-header": !this._config.title,
})}" })}"
.content="${this._config.content}" .content="${this._content}"
></ha-markdown> ></ha-markdown>
</ha-card> </ha-card>
`; `;
} }
private async _connect() {
if (!this._unsubRenderTemplate && this._hass && this._config) {
this._unsubRenderTemplate = subscribeRenderTemplate(
this._hass.connection,
(result) => {
this._content = result;
},
{
template: this._config.content,
entity_ids: this._config.entity_id,
}
);
this._unsubRenderTemplate.catch(() => {
this._content = this._config!.content;
this._unsubRenderTemplate = undefined;
});
}
}
private async _disconnect() {
if (this._unsubRenderTemplate) {
try {
const unsub = await this._unsubRenderTemplate;
this._unsubRenderTemplate = undefined;
await unsub();
} catch (e) {
if (e.code === "not_found") {
// If we get here, the connection was probably already closed. Ignore.
} else {
throw e;
}
}
}
}
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
ha-markdown { ha-markdown {

View File

@ -118,6 +118,8 @@ export interface MarkdownCardConfig extends LovelaceCardConfig {
type: "markdown"; type: "markdown";
content: string; content: string;
title?: string; title?: string;
card_size?: number;
entity_ids?: string | string[];
} }
export interface MediaControlCardConfig extends LovelaceCardConfig { export interface MediaControlCardConfig extends LovelaceCardConfig {