Skip to content

Commit

Permalink
Add template badge (#1475)
Browse files Browse the repository at this point in the history
* Add template badge

* Fix css

* Fix text only and icon only badge

* Add support for weather icon and add documentation

* Improve doc with image

* Improve doc

* Improve doc
  • Loading branch information
piitaya authored Jul 23, 2024
1 parent 51e720d commit a4adc33
Show file tree
Hide file tree
Showing 16 changed files with 681 additions and 33 deletions.
47 changes: 47 additions & 0 deletions docs/badges/template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Template badge

![Template light](../images/template-badge-light.png)
![Template dark](../images/template-badge-dark.png)

## Description

A template badge allows you to build a custom badge. You can use `entity` as a variable for the entity set on the badge e.g. `{{ states(entity) }}`.

> [!WARNING]
> Home Assistant **2024.8** is required to use custom badges.
## Configuration variables

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

| Name | Type | Default | Description |
| :------------------ | :-------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------- |
| `entity` | string | Optional | Entity for template and actions |
| `icon` | string | Optional | Icon to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/) \*. |
| `color` | string | Optional | Color to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `label` | string | Optional | Label to render. Only displayed if content is not empty. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `content` | string | Optional | Content 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/). |
| `tap_action` | action | `none` | Home assistant action to perform on tap |
| `hold_action` | action | `none` | Home assistant action to perform on hold |
| `double_tap_action` | action | `none` | Home assistant action to perform on double_tap |
| `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. |

#### Notes

\* You can render weather svg icons using [weather state](https://developers.home-assistant.io/docs/core/entity/weather/#recommended-values-for-state-and-condition) as icon :

- weather-clear-night
- weather-cloudy
- weather-fog
- weather-lightning
- weather-lightning-rainy
- weather-partlycloudy
- weather-pouring
- weather-rainy
- weather-hail
- weather-snowy
- weather-snowy-rainy
- weather-sunny
- weather-windy
- weather-windy-variant
61 changes: 31 additions & 30 deletions docs/cards/template.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,39 @@ 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 |
| :-------------------- | :-------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------- |
| `entity` | string | Optional | Entity for template and actions |
| `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 |
| `double_tap_action` | action | `none` | Home assistant action to perform on double_tap |
| `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. |

#### Notes

\* You can render weather svg icons using [weather state](https://developers.home-assistant.io/docs/core/entity/weather/#recommended-values-for-state-and-condition) as icon :

- weather-clear-night
- weather-cloudy
- weather-fog
- weather-lightning
- weather-lightning-rainy
- weather-partlycloudy
- weather-pouring
- weather-rainy
- weather-hail
- weather-snowy
- weather-snowy-rainy
- weather-sunny
- weather-windy
- weather-windy-variant
- weather-clear-night
- weather-cloudy
- weather-fog
- weather-lightning
- weather-lightning-rainy
- weather-partlycloudy
- weather-pouring
- weather-rainy
- weather-hail
- weather-snowy
- weather-snowy-rainy
- weather-sunny
- weather-windy
- weather-windy-variant
Binary file added docs/images/template-badge-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/template-badge-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/badges/template/const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PREFIX_NAME } from "../../const";

export const TEMPLATE_BADGE_NAME = `${PREFIX_NAME}-template-badge`;
export const TEMPLATE_BADGE_EDITOR_NAME = `${TEMPLATE_BADGE_NAME}-editor`;
32 changes: 32 additions & 0 deletions src/badges/template/template-badge-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { array, assign, object, optional, string, union } from "superstruct";
import { LovelaceBadgeConfig } from "../../ha";
import {
ActionsSharedConfig,
actionsSharedConfigStruct,
} from "../../shared/config/actions-config";
import { lovelaceBadgeConfigStruct } from "../../shared/config/lovelace-badge-config";

export type TemplateBadgeConfig = LovelaceBadgeConfig &
ActionsSharedConfig & {
entity?: string;
icon?: string;
color?: string;
label?: string;
content?: string;
picture?: string;
entity_id?: string | string[];
};

export const templateBadgeConfigStruct = assign(
lovelaceBadgeConfigStruct,
actionsSharedConfigStruct,
object({
entity: optional(string()),
icon: optional(string()),
color: optional(string()),
label: optional(string()),
content: optional(string()),
picture: optional(string()),
entity_id: optional(union([string(), array(string())])),
})
);
99 changes: 99 additions & 0 deletions src/badges/template/template-badge-editor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { html, nothing } from "lit";
import { customElement, state } from "lit/decorators.js";
import { assert } from "superstruct";
import { LovelaceBadgeEditor, fireEvent } from "../../ha";
import setupCustomlocalize from "../../localize";
import { computeActionsFormSchema } from "../../shared/config/actions-config";
import { MushroomBaseElement } from "../../utils/base-element";
import { GENERIC_LABELS } from "../../utils/form/generic-fields";
import { HaFormSchema } from "../../utils/form/ha-form";
import { loadHaComponents } from "../../utils/loader";
import { TEMPLATE_BADGE_EDITOR_NAME } from "./const";
import {
TemplateBadgeConfig,
templateBadgeConfigStruct,
} from "./template-badge-config";

export const TEMPLATE_LABELS = ["content", "label", "picture"];

const SCHEMA: HaFormSchema[] = [
{ name: "entity", selector: { entity: {} } },
{
name: "icon",
selector: { template: {} },
},
{
name: "color",
selector: { template: {} },
},
{
name: "label",
selector: { template: {} },
},
{
name: "content",
selector: { template: {} },
},
{
name: "picture",
selector: { template: {} },
},
...computeActionsFormSchema(),
];

@customElement(TEMPLATE_BADGE_EDITOR_NAME)
export class TemplateBadgeEditor
extends MushroomBaseElement
implements LovelaceBadgeEditor
{
@state() private _config?: TemplateBadgeConfig;

connectedCallback() {
super.connectedCallback();
void loadHaComponents();
}

public setConfig(config: TemplateBadgeConfig): void {
assert(config, templateBadgeConfigStruct);
this._config = config;
}

private _computeLabel = (schema: HaFormSchema) => {
const customLocalize = setupCustomlocalize(this.hass!);

if (schema.name === "entity") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${customLocalize("editor.card.template.entity_extra")})`;
}
if (GENERIC_LABELS.includes(schema.name)) {
return customLocalize(`editor.card.generic.${schema.name}`);
}
if (TEMPLATE_LABELS.includes(schema.name)) {
return customLocalize(`editor.card.template.${schema.name}`);
}
return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
};

protected render() {
if (!this.hass || !this._config) {
return nothing;
}

return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.schema=${SCHEMA}
.computeLabel=${this._computeLabel}
@value-changed=${this._valueChanged}
></ha-form>
`;
}

private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
}
}
Loading

0 comments on commit a4adc33

Please sign in to comment.