Update custom cards, add custom badges and custom card features (#2258)

* Add custom card feature doc

* Add layout options in custom card documentation

* Update example

* Add custom badge documentation

* Update docs/frontend/custom-ui/custom-card.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/frontend/custom-ui/custom-card.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/frontend/custom-ui/custom-card-feature.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/frontend/custom-ui/custom-card.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/frontend/custom-ui/custom-badge.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/frontend/custom-ui/custom-badge.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/frontend/custom-ui/custom-badge.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/frontend/custom-ui/custom-card-feature.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/frontend/custom-ui/custom-card-feature.md

* Apply suggestions from code review

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Paul Bottein 2024-07-24 22:10:09 +02:00 committed by GitHub
parent 117ffb7c80
commit a34195da26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 302 additions and 121 deletions

View File

@ -0,0 +1,131 @@
---
title: "Custom badge"
---
[Badges](https://www.home-assistant.io/dashboards/badges/) are small widgets that sit at the top of a view, above all cards. We offer a built-in badge, the [entity badge](https://next.home-assistant.io/dashboards/badges/#entity-badge), but you're not just limited that one. You can build and use your own!
## Defining your badge
Defining a badge is done in a very similar way to defining a [custom card](/docs/frontend/custom-ui/custom-card).
Let's create a basic badge that displays custom text at the top of the screen.
Create a new file in your Home Assistant config dir as `<config>/www/text-badge.js` and put in the following contents:
```js
class TextBadge extends HTMLElement {
// Whenever the state changes, a new `hass` object is set. Use this to
// update your content.
set hass(hass) {
this._hass = hass;
this.updateContent();
}
// The user supplied configuration. Throw an exception and Home Assistant
// will render an error badge.
setConfig(config) {
if (!config.entity) {
throw new Error("You need to define an entity");
}
this.config = config;
this.updateContent();
}
updateContent() {
if (!this.config || !this._hass) return;
const entityId = this.config.entity;
const state = this._hass.states[entityId];
const stateStr = state ? state.state : "unavailable";
this.innerHTML = `<p>${stateStr}</p>`;
}
}
customElements.define("text-badge", TextBadge);
```
## Referencing your new badge
In our example badge, we defined a badge with the tag `text-badge` (see last line), so our badge type will be `custom:text-badge`. And because you created the file in your `<config>/www` directory, it will be accessible in your browser via the url `/local/` (if you have recently added the www folder you will need to re-start Home Assistant for files to be picked up).
Add a resource to your dashboard configuration with URL `/local/text-badge.js` and type `module` ([resource docs](/docs/frontend/custom-ui/registering-resources)).
You can then use your badge in your dashboard configuration:
```yaml
# Example dashboard configuration
views:
- name: Example
badges:
- type: "custom:text-badge"
entity: light.bedside_lamp
```
## API
Custom badges are defined as a [custom element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements). It's up to you to decide how to render your DOM inside your element. You can use Polymer, Angular, Preact or any other popular framework (except for React [more info on React here](https://custom-elements-everywhere.com/#react)).
Home Assistant will call `setConfig(config)` when the configuration changes (rare). If you throw an exception if the configuration is invalid, Home Assistant will render an error badge to notify the user.
Home Assistant will set [the `hass` property](/docs/frontend/data/) when the state of Home Assistant changes (frequent). Whenever the state changes, the component will have to update itself to represent the latest state.
## Graphical badge configuration
Your badge can define a `getConfigElement` method that returns a custom element for editing the user configuration. Home Assistant will display this element in the badge editor in the dashboard.
Your badge can also define a `getStubConfig` method that returns a default badge configuration (without the `type:` parameter) in json form for use by the badge type picker in the dashboard.
Home Assistant will call the `setConfig` method of the config element on setup.
Home Assistant will update the `hass` property of the config element on state changes, and the `lovelace` element, which contains information about the dashboard configuration.
Changes to the configuration are communicated back to the dashboard by dispatching a `config-changed` event with the new configuration in its detail.
To have your badge displayed in the badge picker dialog in the dashboard, add an object describing it to the array `window.customBadges`. Required properties of the object are `type` and `name` (see example below).
```js
import "./text-badge-editor.js";
class TextBadge extends HTMLElement {
...
static getConfigElement() {
return document.createElement("text-badge-editor");
}
static getStubConfig() {
return { entity: "sun.sun" };
}
}
customElements.define("text-badge", TextBadge);
```
```js
class TextBadgeEditor extends HTMLElement {
setConfig(config) {
this._config = config;
}
configChanged(newConfig) {
const event = new Event("config-changed", {
bubbles: true,
composed: true,
});
event.detail = { config: newConfig };
this.dispatchEvent(event);
}
}
customElements.define("text-badge-editor", TextBadgeEditor);
window.customBadges = window.customBadges || [];
window.customBadges.push({
type: "text-badge",
name: "Text badge",
preview: false, // Optional - defaults to false
description: "A custom badge made by me!", // Optional
documentationURL:
"https://developers.home-assistant.io/docs/frontend/custom-ui/custom-badge", // Adds a help link in the frontend badge editor
});
```

View File

@ -0,0 +1,116 @@
---
title: "Custom card feature"
---
Some dashboard cards have support for [features](https://www.home-assistant.io/dashboards/features/). These widgets add quick controls to the card. We offer a lot of built-in features, but you're not just limited to the ones that we decided to include in Home Assistant. You can build and use your own in the same way as defining [custom cards](/docs/frontend/custom-ui/custom-card).
## Defining your card feature
Below is an example of a custom card feature for [button entity](/docs/core/entity/button/).
![Screenshot of the custom card feature example](/img/en/frontend/dashboard-custom-card-feature-screenshot.png)
```js
import {
LitElement,
html,
css,
} from "https://unpkg.com/lit-element@2.0.1/lit-element.js?module";
const supportsButtonPressCardFeature = (stateObj) => {
const domain = stateObj.entity_id.split(".")[0];
return domain === "button";
};
class ButtonPressCardFeature extends LitElement {
static get properties() {
return {
hass: undefined,
config: undefined,
stateObj: undefined,
};
}
static getStubConfig() {
return {
type: "custom:button-press-card-feature",
label: "Press",
};
}
setConfig(config) {
if (!config) {
throw new Error("Invalid configuration");
}
this.config = config;
}
_press(ev) {
ev.stopPropagation();
this.hass.callService("button", "press", {
entity_id: this.stateObj.entity_id,
});
}
render() {
if (
!this.config ||
!this.hass ||
!this.stateObj ||
!supportsButtonPressCardFeature(this.stateObj)
) {
return null;
}
return html`
<button class="button" @click=${this._press}>
${this.config.label || "Press"}
</button>
`;
}
static get styles() {
return css`
.button {
display: block;
height: var(--feature-height, 42px);
width: 100%;
border-radius: var(--feature-border-radius, 12px);
border: none;
background-color: #eeeeee;
cursor: pointer;
transition: background-color 180ms ease-in-out;
}
.button:hover {
background-color: #dddddd;
}
.button:focus {
background-color: #cdcdcd;
}
`;
}
}
customElements.define("button-press-card-feature", ButtonPressCardFeature);
window.customCardFeatures = window.customCardFeatures || [];
window.customCardFeatures.push({
type: "button-press-card-feature",
name: "Button press",
supported: supportsButtonPressCardFeature, // Optional
configurable: true, // Optional - defaults to false
});
```
If you want your feature to better integrate with the default design of home assistant, you can use these CSS variables:
- `--feature-height`: Recommended height (42px).
- `--feature-border-radius`: Recommended border radius (12px). It be can useful to set button or slider border radius.
- `--feature-button-spacing`: Recommended space between buttons (12px). It can be useful if you have multiple buttons in your feature.
The main difference with custom cards is the graphical configuration option.
To have it displayed in the card editor, you must add an object describing it to the array `window.customCardFeatures`.
Required properties of the object are `type` and `name`. It is recommended to define the `supported` option with a function, so the editor can only propose the feature if it is compatible with the selected entity in the card. Set `configurable` to `true` if your entity has additional configuration (e.g. `label` option in the example above) so the editor.
Also, the static functions `getConfigElement` and `getStubConfig` work the same as with normal custom cards.

View File

@ -1,5 +1,5 @@
---
title: "Custom cards"
title: "Custom card"
---
[Dashboards](https://www.home-assistant.io/dashboards/) are our approach to defining your user interface for Home Assistant. We offer a lot of built-in cards, but you're not just limited to the ones that we decided to include in Home Assistant. You can build and use your own!
@ -46,10 +46,20 @@ class ContentCardExample extends HTMLElement {
}
// The height of your card. Home Assistant uses this to automatically
// distribute all cards over the available columns.
// distribute all cards over the available columns in masonry view
getCardSize() {
return 3;
}
// The rules for your card for sizing your card if the grid in section view
getLayoutOptions() {
return {
grid_rows: 3,
grid_columns: 2,
grid_min_rows: 3,
grid_max_rows: 3,
};
}
}
customElements.define("content-card-example", ContentCardExample);
@ -76,11 +86,15 @@ views:
Custom cards are defined as a [custom element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements). It's up to you to decide how to render your DOM inside your element. You can use Polymer, Angular, Preact or any other popular framework (except for React [more info on React here](https://custom-elements-everywhere.com/#react)).
### Configuration
Home Assistant will call `setConfig(config)` when the configuration changes (rare). If you throw an exception if the configuration is invalid, Home Assistant will render an error card to notify the user.
Home Assistant will set [the `hass` property](/docs/frontend/data/) when the state of Home Assistant changes (frequent). Whenever the state changes, the component will have to update itself to represent the latest state.
Your card can define a `getCardSize` method that returns the size of your card as a number or a promise that will resolve to a number. A height of 1 is equivalent to 50 pixels. This will help Home Assistant distribute the cards evenly over the columns. A card size of `1` will be assumed if the method is not defined.
### Sizing in masonry view
Your card can define a `getCardSize` method that returns the size of your card as a number or a promise that will resolve to a number. A height of 1 is equivalent to 50 pixels. This will help Home Assistant distribute the cards evenly over the columns in the [masonry view](https://www.home-assistant.io/dashboards/masonry/). A card size of `1` will be assumed if the method is not defined.
Since some elements can be lazy loaded, if you want to get the card size of another element, you should first check it is defined.
@ -90,7 +104,39 @@ return customElements
.then(() => element.getCardSize());
```
Your card can define a `getConfigElement` method that returns a custom element for editing the user configuration. Home Assistant will display this element in the card editor in the dashboard.
### Sizing in sections view
You card can define a `getLayoutOptions` method that returns the min, max and default number of cells your card will take in the grid if your card is used if the [sections view](https://www.home-assistant.io/dashboards/masonry/)
If you don't define this method, the card will take 4 columns (full width) and will ignore the rows of the grid.
A cell of the grid is defined with the following dimension:
- width: between `80px` and `120px` depending on the screen size
- height: `56px`
- gap between cells: `8px`
The different layout options are:
- `grid_rows`: Default number of rows the card takes
- `grid_min_rows`: Minimal number of rows the card takes
- `grid_max_rows`: Maximal number of rows the card takes
- `grid_columns`: Default number of columns the card takes
- `grid_min_columns`: Minimal number of columns the card takes
- `grid_max_columns`: Maximal number of columns the card takes
Example of implementation:
```js
public getLayoutOptions() {
return {
grid_rows: 2,
grid_columns: 2,
grid_min_rows: 2,
};
}
```
In this example, the card will take 2 x 2 cells by default. The height of the card cannot be smaller than 2 rows. According to the cell dimension, the card will have a height of `120px` (`2` * `56px` + `8px`).
## Advanced example
@ -267,121 +313,7 @@ window.customCards.push({
name: "Content Card",
preview: false, // Optional - defaults to false
description: "A custom card made by me!", // Optional
documentationURL: "https://developers.home-assistant.io/docs/frontend/custom-ui/custom-card/", // Adds a help link in the frontend card editor
documentationURL:
"https://developers.home-assistant.io/docs/frontend/custom-ui/custom-card", // Adds a help link in the frontend card editor
});
```
## Tile features
The tile card has support for "features" to add quick actions to control the entity. We offer some built-in features, but you can build and use your own using similar way than defining custom cards.
Below is an example of a custom tile feature for [button entity](/docs/core/entity/button/).
![Screenshot of the custom tile feature example](/img/en/frontend/dashboard-custom-tile-feature-screenshot.png)
```js
import {
LitElement,
html,
css,
} from "https://unpkg.com/lit-element@2.0.1/lit-element.js?module";
const supportsButtonPressTileFeature = (stateObj) => {
const domain = stateObj.entity_id.split(".")[0];
return domain === "button";
};
class ButtonPressTileFeature extends LitElement {
static get properties() {
return {
hass: undefined,
config: undefined,
stateObj: undefined,
};
}
static getStubConfig() {
return {
type: "custom:button-press-tile-feature",
label: "Press",
};
}
setConfig(config) {
if (!config) {
throw new Error("Invalid configuration");
}
this.config = config;
}
_press(ev) {
ev.stopPropagation();
this.hass.callService("button", "press", {
entity_id: this.stateObj.entity_id,
});
}
render() {
if (
!this.config ||
!this.hass ||
!this.stateObj ||
!supportsButtonPressTileFeature(this.stateObj)
) {
return null;
}
return html`
<div class="container">
<button class="button" @click=${this._press}>
${this.config.label || "Press"}
</button>
</div>
`;
}
static get styles() {
return css`
.container {
display: flex;
flex-direction: row;
padding: 0 12px 12px 12px;
width: auto;
}
.button {
display: block;
width: 100%;
height: 40px;
border-radius: 6px;
border: none;
background-color: #eeeeee;
cursor: pointer;
transition: background-color 180ms ease-in-out;
}
.button:hover {
background-color: #dddddd;
}
.button:focus {
background-color: #cdcdcd;
}
`;
}
}
customElements.define("button-press-tile-feature", ButtonPressTileFeature);
window.customTileFeatures = window.customTileFeatures || [];
window.customTileFeatures.push({
type: "button-press-tile-feature",
name: "Button press",
supported: supportsButtonPressTileFeature, // Optional
configurable: true, // Optional - defaults to false
});
```
The only difference with custom cards is the graphical configuration option.
To have it displayed in the tile card editor, you must add an object describing it to the array `window.customTileFeatures`.
Required properties of the object are `type` and `name`. It is recommended to define the `supported` option with a function so the editor can only propose the feature if it is compatible with the selected entity in the tile card. Set `configurable` to `true` if your entity has additional configuration (e.g. `label` option in the example above) so the editor.
Also, the static functions `getConfigElement` and `getStubConfig` work the same as with normal custom maps.

View File

@ -40,6 +40,8 @@ module.exports = {
label: "Custom UI",
items: [
"frontend/custom-ui/custom-card",
"frontend/custom-ui/custom-card-feature",
"frontend/custom-ui/custom-badge",
"frontend/custom-ui/custom-strategy",
"frontend/custom-ui/custom-view",
"frontend/custom-ui/creating-custom-panels",
@ -287,7 +289,7 @@ module.exports = {
"asyncio_categorizing_functions",
"asyncio_working_with_async",
"asyncio_thread_safety",
"asyncio_blocking_operations"
"asyncio_blocking_operations",
],
},
],

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB