diff --git a/docs/frontend/custom-ui/custom-card.md b/docs/frontend/custom-ui/custom-card.md
index 129184b7..49967107 100644
--- a/docs/frontend/custom-ui/custom-card.md
+++ b/docs/frontend/custom-ui/custom-card.md
@@ -22,12 +22,12 @@ class ContentCardExample extends HTMLElement {
`;
- this.content = this.querySelector('div');
+ this.content = this.querySelector("div");
}
const entityId = this.config.entity;
const state = hass.states[entityId];
- const stateStr = state ? state.state : 'unavailable';
+ const stateStr = state ? state.state : "unavailable";
this.content.innerHTML = `
The state of ${entityId} is ${stateStr}!
@@ -40,7 +40,7 @@ class ContentCardExample extends HTMLElement {
// will render an error card.
setConfig(config) {
if (!config.entity) {
- throw new Error('You need to define an entity');
+ throw new Error("You need to define an entity");
}
this.config = config;
}
@@ -52,7 +52,7 @@ class ContentCardExample extends HTMLElement {
}
}
-customElements.define('content-card-example', ContentCardExample);
+customElements.define("content-card-example", ContentCardExample);
```
## Referencing your new card
@@ -66,10 +66,10 @@ You can then use your card in your dashboard configuration:
```yaml
# Example dashboard configuration
views:
-- name: Example
- cards:
- - type: "custom:content-card-example"
- entity: input_boolean.switch_tv
+ - name: Example
+ cards:
+ - type: "custom:content-card-example"
+ entity: input_boolean.switch_tv
```
## API
@@ -85,9 +85,9 @@ Your card can define a `getCardSize` method that returns the size of your card a
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.
```js
- return customElements
- .whenDefined(element.localName)
- .then(() => element.getCardSize());
+return customElements
+ .whenDefined(element.localName)
+ .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.
@@ -106,7 +106,7 @@ import "https://unpkg.com/wired-toggle@0.8.0/wired-toggle.js?module";
import {
LitElement,
html,
- css
+ css,
} from "https://unpkg.com/lit-element@2.0.1/lit-element.js?module";
function loadCSS(url) {
@@ -123,14 +123,14 @@ class WiredToggleCard extends LitElement {
static get properties() {
return {
hass: {},
- config: {}
+ config: {},
};
}
render() {
return html`
- ${this.config.entities.map(ent => {
+ ${this.config.entities.map((ent) => {
const stateObj = this.hass.states[ent];
return stateObj
? html`
@@ -138,13 +138,11 @@ class WiredToggleCard extends LitElement {
${stateObj.attributes.friendly_name}
this._toggle(stateObj)}"
+ @change="${(ev) => this._toggle(stateObj)}"
>
`
- : html`
-
Entity ${ent} not found.
- `;
+ : html`
Entity ${ent} not found.
`;
})}
`;
@@ -165,7 +163,7 @@ class WiredToggleCard extends LitElement {
_toggle(state) {
this.hass.callService("homeassistant", "toggle", {
- entity_id: state.entity_id
+ entity_id: state.entity_id,
});
}
@@ -208,13 +206,13 @@ And for your configuration:
```yaml
# Example dashboard configuration
views:
-- name: Example
- cards:
- - type: "custom:wired-toggle-card"
- entities:
- - input_boolean.switch_ac_kitchen
- - input_boolean.switch_ac_livingroom
- - input_boolean.switch_tv
+ - name: Example
+ cards:
+ - type: "custom:wired-toggle-card"
+ entities:
+ - input_boolean.switch_ac_kitchen
+ - input_boolean.switch_ac_livingroom
+ - input_boolean.switch_tv
```
## Graphical card configuration
@@ -248,7 +246,6 @@ customElements.define('content-card-example', ContentCardExample);
```js
class ContentCardEditor extends LitElement {
-
setConfig(config) {
this._config = config;
}
@@ -256,9 +253,9 @@ class ContentCardEditor extends LitElement {
configChanged(newConfig) {
const event = new Event("config-changed", {
bubbles: true,
- composed: true
+ composed: true,
});
- event.detail = {config: newConfig};
+ event.detail = { config: newConfig };
this.dispatchEvent(event);
}
}
@@ -269,10 +266,125 @@ window.customCards.push({
type: "content-card-example",
name: "Content Card",
preview: false, // Optional - defaults to false
- description: "A custom card made by me!" // Optional
+ description: "A custom card made by me!", // Optional
});
```
+## 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/).
+
+
+
+```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`
+
+
+
+ `;
+ }
+
+ 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.
+
## Advanced Resources
Community Maintained Boilerplate Card - Advanced Template (Typescript, Rollup, Linting, etc.)
diff --git a/static/img/en/frontend/dashboard-custom-tile-feature-screenshot.png b/static/img/en/frontend/dashboard-custom-tile-feature-screenshot.png
new file mode 100644
index 00000000..9a55d992
Binary files /dev/null and b/static/img/en/frontend/dashboard-custom-tile-feature-screenshot.png differ