Skip to content

Commit

Permalink
Add custom buttons to template-card
Browse files Browse the repository at this point in the history
  • Loading branch information
martindybal committed May 13, 2024
1 parent 48db88d commit be52ce6
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 23 deletions.
61 changes: 61 additions & 0 deletions .hass_dev/views/template-view.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,67 @@ cards:
picture: /local/mushrooms.jpeg
columns: 2
square: false
- type: vertical-stack
title: Custom buttons
cards:
- type: grid
columns: 2
square: false
cards:
- type: custom:mushroom-template-card
primary: Hello, {{user}}
secondary: How are you?
icon: mdi:home
first_button_icon: mdi:arrow-up
first_button_tap_action:
action: none
second_button_icon: mdi:arrow-right
second_button_tap_action:
action: none
third_button_icon: mdi:arrow-down
third_button_tap_action:
action: none
fourth_button_icon: mdi:arrow-left
fourth_button_tap_action:
action: none
- type: grid
columns: 2
square: false
cards:
- type: custom:mushroom-template-card
primary: Hello, {{user}}
secondary: How are you?
icon: mdi:home
layout: vertical
first_button_icon: mdi:arrow-up
first_button_tap_action:
action: none
second_button_icon: mdi:arrow-right
second_button_tap_action:
action: none
third_button_icon: mdi:arrow-down
third_button_tap_action:
action: none
fourth_button_icon: mdi:arrow-left
fourth_button_tap_action:
action: none
- type: custom:mushroom-template-card
primary: Hello, {{user}}
secondary: How are you?
icon: mdi:home
layout: horizontal
first_button_icon: mdi:arrow-up
first_button_tap_action:
action: none
second_button_icon: mdi:arrow-right
second_button_tap_action:
action: none
third_button_icon: mdi:arrow-down
third_button_tap_action:
action: none
fourth_button_icon: mdi:arrow-left
fourth_button_tap_action:
action: none
- type: vertical-stack
title: Layout
cards:
Expand Down
40 changes: 24 additions & 16 deletions docs/cards/template.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,30 @@ A template card allows you to build a custom card. You can use `entity` as a var

All the options are available in the lovelace editor but you can use `yaml` if you want.

| Name | Type | Default | Description |
| :-------------------- | :-------------- | :---------- | :---------------------------------------------------------------------------------------------------------------------------------- |
| `icon` | string | Optional | Icon to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/) \*. |
| `icon_color` | string | Optional | Icon color to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `primary` | string | Optional | Primary info to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `secondary` | string | Optional | Secondary info to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `badge_icon` | string | Optional | Badge icon to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `badge_color` | string | Optional | Badge icon color to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `picture` | string | Optional | Picture to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `multiline_secondary` | boolean | `false` | Enables support for multiline text for the secondary info. |
| `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 |
| `tap_action` | action | `none` | Home assistant action to perform on tap |
| `hold_action` | action | `none` | Home assistant action to perform on hold |
| `entity_id` | `string` `list` | Optional | Only reacts to the state changes of these entities. This can be used if the automatic analysis fails to find all relevant entities. |
| `double_tap_action` | action | `more-info` | Home assistant action to perform on double_tap |
| Name | Type | Default | Description |
| :--------------------------| :-------------- | :---------- | :---------------------------------------------------------------------------------------------------------------------------------- |
| `icon` | string | Optional | Icon to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/) \*. |
| `icon_color` | string | Optional | Icon color to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `primary` | string | Optional | Primary info to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `secondary` | string | Optional | Secondary info to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `badge_icon` | string | Optional | Badge icon to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `badge_color` | string | Optional | Badge icon color to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `picture` | string | Optional | Picture to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `multiline_secondary` | boolean | `false` | Enables support for multiline text for the secondary info. |
| `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 |
| `tap_action` | action | `none` | Home assistant action to perform on tap |
| `hold_action` | action | `none` | Home assistant action to perform on hold |
| `entity_id` | `string` `list` | Optional | Only reacts to the state changes of these entities. This can be used if the automatic analysis fails to find all relevant entities. |
| `double_tap_action` | action | `more-info` | Home assistant action to perform on double_tap |
| `first_button_icon` | string | Optional | First custom button icon. Allows you to add a custom button if the icon and action is set. |
| `first_button_tap_action` | action | Optional | First custom button tap action. Allows you to add a custom button if the icon and action is set. |
| `second_button_icon` | string | Optional | Second custom button icon. Allows you to add a custom button if the icon and action is set. |
| `second_button_tap_action` | action | Optional | Second custom button tap action. Allows you to add a custom button if the icon and action is set. |
| `third_button_icon` | string | Optional | Third custom button icon. Allows you to add a custom button if the icon and action is set. |
| `third_button_tap_action` | action | Optional | Third custom button tap action. Allows you to add a custom button if the icon and action is set. |
| `fourth_button_icon` | string | Optional | Fourth custom button icon. Allows you to add a custom button if the icon and action is set. |
| `fourth_button_tap_action` | action | Optional | Fourth custom button tap action. Allows you to add a custom button if the icon and action is set. |

#### Notes

Expand Down
19 changes: 18 additions & 1 deletion src/cards/template-card/template-card-config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { array, assign, boolean, object, optional, string, union } from "superstruct";
import { LovelaceCardConfig } from "../../ha";
import { ActionConfig, actionConfigStruct, LovelaceCardConfig } from "../../ha";
import { ActionsSharedConfig, actionsSharedConfigStruct } from "../../shared/config/actions-config";
import {
AppearanceSharedConfig,
Expand All @@ -20,6 +20,15 @@ export type TemplateCardConfig = LovelaceCardConfig &
picture?: string;
multiline_secondary?: boolean;
entity_id?: string | string[];

first_button_icon?: string;
first_button_tap_action?: ActionConfig;
second_button_icon?: string;
second_button_tap_action?: ActionConfig;
third_button_icon?: string;
third_button_tap_action?: ActionConfig;
fourth_button_icon?: string;
fourth_button_tap_action?: ActionConfig;
};

export const templateCardConfigStruct = assign(
Expand All @@ -36,5 +45,13 @@ export const templateCardConfigStruct = assign(
picture: optional(string()),
multiline_secondary: optional(boolean()),
entity_id: optional(union([string(), array(string())])),
first_button_icon: optional(string()),
first_button_tap_action: optional(actionConfigStruct),
second_button_icon: optional(string()),
second_button_tap_action: optional(actionConfigStruct),
third_button_icon: optional(string()),
third_button_tap_action: optional(actionConfigStruct),
fourth_button_icon: optional(string()),
fourth_button_tap_action: optional(actionConfigStruct)
})
);
36 changes: 35 additions & 1 deletion src/cards/template-card/template-card-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import { HaFormSchema } from "../../utils/form/ha-form";
import { loadHaComponents } from "../../utils/loader";
import { TEMPLATE_CARD_EDITOR_NAME } from "./const";
import { TemplateCardConfig, templateCardConfigStruct } from "./template-card-config";
import { UiAction } from "../../utils/form/ha-selector";

export const customButtonLabels = (buttonPrefix: string): string[] => {
return [
`${buttonPrefix}_button_icon`,
`${buttonPrefix}_button_tap_action`,
];
};

export const TEMPLATE_LABELS = [
"badge_icon",
Expand All @@ -19,10 +27,32 @@ export const TEMPLATE_LABELS = [
"secondary",
"multiline_secondary",
"picture",
...customButtonLabels("first"),
...customButtonLabels("second"),
...customButtonLabels("third"),
...customButtonLabels("fourth"),
];

const buttonAllowedActions: UiAction[] = ["navigate", "url", "call-service", "assist", "none"];

export const customButtonFormSchema = (buttonPrefix: string): HaFormSchema[] => {
return [
{
name: `${buttonPrefix}_button_icon`,
selector: { template: {} },
},
{
name: `${buttonPrefix}_button_tap_action`,
selector: { "ui-action": { actions: buttonAllowedActions } },
}
];
};

const SCHEMA: HaFormSchema[] = [
{ name: "entity", selector: { entity: {} } },
{
name: "entity",
selector: { entity: {} }
},
{
name: "icon",
selector: { template: {} },
Expand Down Expand Up @@ -61,6 +91,10 @@ const SCHEMA: HaFormSchema[] = [
],
},
...computeActionsFormSchema(),
...customButtonFormSchema("first"),
...customButtonFormSchema("second"),
...customButtonFormSchema("third"),
...customButtonFormSchema("fourth"),
];

@customElement(TEMPLATE_CARD_EDITOR_NAME)
Expand Down
78 changes: 75 additions & 3 deletions src/cards/template-card/template-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { customElement, property, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import { styleMap } from "lit/directives/style-map.js";
import {
ActionConfig,
actionHandler,
ActionHandlerEvent,
computeRTL,
Expand All @@ -28,13 +29,20 @@ import { getWeatherSvgIcon } from "../../utils/icons/weather-icon";
import { weatherSVGStyles } from "../../utils/weather";
import { TEMPLATE_CARD_EDITOR_NAME, TEMPLATE_CARD_NAME } from "./const";
import { TemplateCardConfig } from "./template-card-config";
import { Layout } from "../../utils/layout";

registerCustomCard({
type: TEMPLATE_CARD_NAME,
name: "Mushroom Template Card",
description: "Card for custom rendering with templates",
});

export const customButtonKeys = (buttonPrefix: string): string[] => {
return [
`${buttonPrefix}_button_icon`,
`${buttonPrefix}_button_tap_action`,
];
};
const TEMPLATE_KEYS = [
"icon",
"icon_color",
Expand All @@ -43,6 +51,10 @@ const TEMPLATE_KEYS = [
"primary",
"secondary",
"picture",
...customButtonKeys("first"),
...customButtonKeys("second"),
...customButtonKeys("third"),
...customButtonKeys("fourth"),
] as const;
type TemplateKey = (typeof TEMPLATE_KEYS)[number];

Expand Down Expand Up @@ -126,6 +138,30 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard {
};
}

private get _custom_buttons(): Button[] {
if (!this._config) return [];

const buttons: Button[] = [];

if (this._config.first_button_icon && this._config.first_button_tap_action) {
buttons.push(new Button(this._config.first_button_icon, this._config.first_button_tap_action));
}

if (this._config.second_button_icon && this._config.second_button_tap_action) {
buttons.push(new Button(this._config.second_button_icon, this._config.second_button_tap_action));
}

if (this._config.third_button_icon && this._config.third_button_tap_action) {
buttons.push(new Button(this._config.third_button_icon, this._config.third_button_tap_action));
}

if (this._config.fourth_button_icon && this._config.fourth_button_tap_action) {
buttons.push(new Button(this._config.fourth_button_icon, this._config.fourth_button_tap_action));
}

return buttons;
}

public connectedCallback() {
super.connectedCallback();
this._tryConnect();
Expand All @@ -140,14 +176,14 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard {
}

public isTemplate(key: TemplateKey) {
const value = this._config?.[key];
const value = this._config?.[key] as string;
return value?.includes("{");
}

private getValue(key: TemplateKey) {
return this.isTemplate(key)
? this._templateResults[key]?.result?.toString()
: this._config?.[key];
: this._config?.[key] as string | undefined;
}

protected render() {
Expand Down Expand Up @@ -206,6 +242,9 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard {
.multiline_secondary=${multiline_secondary}
></mushroom-state-info>
</mushroom-state-item>
${this._custom_buttons.length > 0
? this.renderCustomButtons(rtl, appearance.layout)
: nothing}
</mushroom-card>
</ha-card>
`;
Expand Down Expand Up @@ -249,6 +288,34 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard {
`;
}

renderCustomButtons(rtl: boolean, layout?: Layout): TemplateResult {
return html`
<div class="actions" ?rtl=${rtl}>
<mushroom-button-group .fill=${layout !== "horizontal"} ?rtl=${rtl}>
${this._custom_buttons.map(this.renderCustomButton.bind(this))}
</mushroom-button-group>
</div>
`;
}

renderCustomButton(button: Button): TemplateResult {
var handleButtonTap = (ev: ActionHandlerEvent) => {
ev.stopPropagation();

const config = {
tap_action: button.tap_action,
};
handleAction(this, this.hass!, config, ev.detail.action!);
}

return html`
<mushroom-button @action=${handleButtonTap}
.actionHandler=${actionHandler()}>
<ha-icon .icon=${button.icon}></ha-icon>
</mushroom-button>
`;
}

protected updated(changedProps: PropertyValues): void {
super.updated(changedProps);
if (!this._config || !this.hass) {
Expand Down Expand Up @@ -284,7 +351,7 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard {
};
},
{
template: this._config[key] ?? "",
template: this._config[key] as string | undefined ?? "",
entity_ids: this._config.entity_id,
variables: {
config: this._config,
Expand Down Expand Up @@ -359,4 +426,9 @@ export class TemplateCard extends MushroomBaseElement implements LovelaceCard {
`,
];
}

}

class Button {
constructor(readonly icon: string, readonly tap_action: ActionConfig) {}
}
10 changes: 9 additions & 1 deletion src/translations/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,15 @@
"content": "Obsah",
"badge_icon": "Ikona odznaku",
"badge_color": "Barva odznaku",
"picture": "Obrázek (nahradí ikonu)"
"picture": "Obrázek (nahradí ikonu)",
"first_button_icon": "Ikona prvního tlačítka",
"first_button_tap_action": "Akce při klepnutí na prvního tlačítko",
"second_button_icon": "Ikona prvního tlačítka",
"second_button_tap_action": "Akce při klepnutí na prvního tlačítko",
"third_button_icon": "Ikona prvního tlačítka",
"third_button_tap_action": "Akce při klepnutí na prvního tlačítko",
"fourth_button_icon": "Ikona prvního tlačítka",
"fourth_button_tap_action": "Akce při klepnutí na prvního tlačítko"
},
"title": {
"title": "Titulek",
Expand Down
Loading

0 comments on commit be52ce6

Please sign in to comment.