Custom strategies
Generate a whole dashboard, a view, or a section from code, and offer your dashboard in the new dashboard dialog.
A strategy is code that writes a dashboard configuration. Instead of a list of cards, the dashboard says "use this strategy", and the strategy generates the cards from what is in the home, every time the dashboard loads. The Overview dashboard Home Assistant builds for you, the areas dashboard, the map, and the energy dashboard are all strategies.
There are three kinds:
- A dashboard strategy generates the whole dashboard, with all its views.
- A view strategy generates one view.
- A section strategy generates one section in a sections view.
They work together. A dashboard strategy often generates a list of views that each use a view strategy, so each view is only built when someone opens it.
A strategy is a custom element with a static generate() method. It gets the strategy configuration and hass, and returns a dashboard configuration:
class MyDemoDashboardStrategy extends HTMLElement {
static async generate(config, hass) {
const name = hass.config.location_name || "Home Assistant";
return {
title: config.title || "My demo dashboard",
views: [
{
title: "Home",
path: "home",
cards: [
{ type: "markdown", content: `# Welcome to ${name}` },
{ type: "entities", entities: ["sun.sun"] },
],
},
],
};
}
}
customElements.define(
"ll-strategy-dashboard-my-demo",
MyDemoDashboardStrategy
);Load it as a resource, and use it as the configuration of a dashboard:
strategy:
type: custom:my-demo
title: Generated dashboardThe tag name decides what kind of strategy it is: ll-strategy-dashboard-<name>, ll-strategy-view-<name>, or ll-strategy-section-<name>. The <name> is what follows custom: in the configuration. Everything in the strategy section, including your own options, is passed to generate() as config.
The built-in strategies are good examples. The map dashboard strategy is a small one.
A view strategy returns the configuration of one view. Use it in a view of any dashboard:
views:
- strategy:
type: custom:my-area
area: living_roomThe view strategy below shows the entities of one area. The dashboard strategy above it creates a view with that view strategy for every area. hass.areas, hass.devices, and hass.entities hold the registries, so neither has to fetch anything.
class MyAreaDashboardStrategy extends HTMLElement {
static async generate(config, hass) {
return {
title: config.title || "Areas",
views: Object.values(hass.areas).map((area) => ({
title: area.name,
path: area.area_id,
strategy: { type: "custom:my-area", area: area.area_id },
})),
};
}
}
class MyAreaViewStrategy extends HTMLElement {
static async generate(config, hass) {
const areaId = config.area;
const areaDevices = new Set(
Object.values(hass.devices)
.filter((device) => device.area_id === areaId)
.map((device) => device.id)
);
const cards = Object.values(hass.entities)
.filter(
(entity) =>
entity.area_id === areaId ||
(!entity.area_id && areaDevices.has(entity.device_id))
)
.map((entity) => ({ type: "tile", entity: entity.entity_id }));
return { cards: [{ type: "grid", cards }] };
}
}
customElements.define(
"ll-strategy-dashboard-my-area",
MyAreaDashboardStrategy
);
customElements.define("ll-strategy-view-my-area", MyAreaViewStrategy);Both use the name my-area: one as a dashboard strategy, one as a view strategy. That is fine, because the kind is part of the tag name.
A strategy runs when the dashboard loads. By default, it runs again when the entity, device, area, or floor registry changes, so a new device shows up without a reload. State changes don't trigger it.
To change that, add a static registryDependencies with the registries your strategy depends on: any of entities, devices, areas, floors, labels, and panels. Use an empty list for a strategy that never needs to run again. For full control, add a static shouldRegenerate(config, oldHass, newHass) that returns true when the strategy should run again.
class MyDemoDashboardStrategy extends HTMLElement {
static registryDependencies = ["areas"];
static async generate(config, hass) {
// ...
}
}Register your dashboard strategy in window.customStrategies, and it appears in the Community dashboards section of the dialog to add a dashboard:
window.customStrategies = window.customStrategies || [];
window.customStrategies.push({
type: "my-demo",
strategyType: "dashboard",
name: "My demo dashboard",
description: "A starter dashboard generated from JavaScript.",
documentationURL: "https://example.com/my-demo-dashboard",
});| Key | Required | Meaning |
|---|---|---|
type |
Yes | The name of the strategy, without custom:. |
strategyType |
Yes | "dashboard". |
name |
The name in the dialog. | |
description |
A short description below the name. | |
documentationURL |
A link to your documentation. The frontend does not show it anywhere yet. |
The resource has to be loaded for the dialog to see it. After adding or updating it, reload the page and open the dialog again.
A dashboard strategy can suggest the title and icon of the new dashboard with a static getCreateSuggestions(hass). Users can still change both before they create it.
static getCreateSuggestions(hass) {
return { title: "My demo dashboard", icon: "mdi:view-dashboard" };
}A strategy can have a visual editor for its options. Return an editor element from the static getConfigElement(). It works like a card editor: Home Assistant calls setConfig() with the strategy configuration and sets hass, and the editor fires config-changed with the new configuration.
Two more static options:
configRequired = true: the strategy can't work without settings. Home Assistant shows the editor before it creates the dashboard.noEditor = true: the strategy has no settings to edit.
class MyDemoDashboardStrategy extends HTMLElement {
static configRequired = true;
static getConfigElement() {
return document.createElement("my-demo-strategy-editor");
}
static async generate(config, hass) {
return {
views: [{ cards: [{ type: "iframe", url: config.url }] }],
};
}
}- Keep
generate()fast. It runs on every load, and the dashboard waits for it. hassalready has the areas, devices, and entities, in a short form. For data it doesn't have, like the full entity registry entries, usehass.callWS(). Do that once, in the dashboard strategy, and pass the result to your view strategies through their configuration.- A strategy can use any card, including your own custom cards.
- Write tests for strategies. They are pure functions from data to configuration, which makes them easy to test.