For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt, and this page is available as Markdown at /developers/frontend/custom-ui/custom-card.md.

自定义卡片

Dashboards 是我们为你定义 Home Assistant 用户界面的方式。我们提供了许多内置卡片,但你并不仅限于我们决定纳入 Home Assistant 的那些。你可以构建并使用自己的卡片!

定义你的卡片

下面是一个展示可能性的基本示例。

在你的 Home Assistant config 目录中创建新文件 <config>/www/content-card-example.js,并放入以下内容:

class ContentCardExample extends HTMLElement {
  // card 连接到 DOM
  connectedCallback() {
    const event = new CustomEvent('context-request', {
      bubbles: true,
      composed: true,
      cancelable: true,
    });

    event.context = 'states'; // HA 的 user context provider 使用的 key
    event.subscribe = true; // 订阅此 context 的后续更新,而不只是获取当前值

    event.callback = this._updateStates;

    this.dispatchEvent(event);

    // 初始渲染
    this._render();
  }

  // 接收 states 更新并检查卡片是否需要重新渲染
  _updateStates = (states, unsubscribe) => {
    // 存储 unsubscribe 函数,以便在卡片从 DOM 中移除时调用
    this._unsubscribe = unsubscribe;

    const entityId = this.config.entity;
    console.log("update states", states, entityId)
    const state = states[entityId];
    const stateString = state ? state.state : "unavailable";
    console.log("stateString", stateString, this.stateString)
    if (this.stateString !== stateString) {
      console.log("stateString changed, re-rendering")
      this.stateString = stateString;
      this._render();
    }
  }

  // 仅在需要时渲染卡片的 html
  _render = () => {
    this.innerHTML = `
      ${this.config.entity || "?"} 的状态是 ${this.stateString || "unavailable"}
      <br><br>
      <img src="http://via.placeholder.com/350x150">
    `;
  }

  // 用户提供的配置。抛出异常,Home Assistant
  // 将渲染一个 error 卡片。
  setConfig(config) {
    if (!config.entity) {
      throw new Error("You need to define an entity");
    }
    this.config = config;
  }

  // 你的卡片高度。Home Assistant 使用此值在 masonry view 中
  // 自动将所有卡片分配到可用的列中
  getCardSize() {
    return 3;
  }

  // 在 sections view 的 grid 中确定卡片大小的规则
  getGridOptions() {
    return {
      rows: 3,
      columns: 6,
      min_rows: 3,
      max_rows: 3,
    };
  }

  disconnectedCallback() {
    if (this._unsubscribe) {
      this._unsubscribe();
      this._unsubscribe = undefined;
    }
  }
}

customElements.define("content-card-example", ContentCardExample);

引用你的新卡片

在我们的示例卡片中,我们定义了一个带 tag content-card-example 的卡片(见最后一行),因此我们的卡片类型将是 custom:content-card-example。并且由于你将文件创建在 <config>/www 目录中,它将可以通过 /local/ URL 在你的浏览器中访问(如果你最近才添加 www 文件夹,需要重启 Home Assistant 才能加载文件)。

在你的 dashboard 配置中添加一个 resource,URL 为 /local/content-card-example.js,类型为 moduleresource docs)。

然后你就可以在 dashboard 配置中使用你的卡片:

# 示例 dashboard 配置
views:
  - name: Example
    cards:
      - type: "custom:content-card-example"
        entity: input_boolean.switch_tv

API

Custom cards 被定义为 custom element。如何渲染你的 element 内部的 DOM 由你决定。你可以使用 Polymer、Angular、Preact 或其他任何主流框架(React 除外 – 更多信息请看这里)。

配置

Home Assistant 会在配置变更时(很少)调用 setConfig(config)。如果配置无效时你抛出异常,Home Assistant 会渲染一个 error 卡片来通知用户。

数据上下文

在示例中,你看到我们使用一个 custom event 来请求 Home Assistant 的 states。这是从 Home Assistant 获取数据并订阅此数据后续更新的推荐方式。详细文档请看这里

在 masonry view 中的 sizing

你的卡片可以定义一个 getCardSize 方法,返回一个数字或一个将 resolve 为数字的 promise,代表你卡片的大小。高度为 1 相当于 50 像素。这将帮助 Home Assistant 在 masonry view 中将卡片均匀分配到各列。如果未定义该方法,将假定卡片大小为 1

由于某些 element 可能会被 lazy load,如果你想获取另一个 element 的卡片大小,应首先检查它是否已定义。

return customElements
  .whenDefined(element.localName)
  .then(() => element.getCardSize());

在 sections view 中的 sizing

你可以定义一个 getGridOptions 方法,返回你卡片在 sections view 中使用的 grid 所占的最小、最大和默认单元格数量。每个 section 分为 12 列。如果你不定义此方法,卡片将占用 12 列,并忽略 grid 的 rows。

grid 中一个单元格的尺寸定义如下:

  • width:section 宽度除以 12(约 30px
  • height:56px
  • 单元格间距:8px

不同的 grid 选项包括:

  • rows:卡片占用的默认行数。如果想让卡片忽略 grid 的 rows,不要定义此值(默认未定义)
  • min_rows:卡片占用的最小行数(默认为 1
  • max_rows:卡片占用的最大行数(默认未定义)
  • columns:卡片占用的默认列数。设为 full 以强制卡片为全宽(默认为 12
  • min_columns:卡片占用的最小列数(默认为 1
  • max_columns:卡片占用的最大列数(默认未定义)

关于列数,非常建议使用 3 的倍数作为默认值(36912),这样你的卡片在 dashboard 上默认会更美观。

实现示例:

public getGridOptions() {
  return {
    rows: 2,
    columns: 6,
    min_rows: 2,
  };
}

在此示例中,卡片默认将占用 6 x 2 个单元格。卡片高度不能小于 2 行。根据单元格尺寸,卡片高度将为 120px2 * 56px + 8px)。

使用 JS modules 的示例

dashboard 中加载的 resources 是作为 JS module import 导入的。下面是一个使用 JS modules 的 custom card 示例,它实现了所有高级功能。

有线卡片截图

在你的 Home Assistant config 目录中创建新文件 <config>/www/wired-cards.js,并放入以下内容:

import "https://unpkg.com/wired-card@0.8.1/wired-card.js?module";
import "https://unpkg.com/wired-toggle@0.8.0/wired-toggle.js?module";
import {
  LitElement,
  html,
  css,
} from "https://unpkg.com/lit-element@2.0.1/lit-element.js?module";

function loadCSS(url) {
  const link = document.createElement("link");
  link.type = "text/css";
  link.rel = "stylesheet";
  link.href = url;
  document.head.appendChild(link);
}

loadCSS("https://fonts.googleapis.com/css?family=Gloria+Hallelujah");

class WiredToggleCard extends LitElement {
  static get properties() {
    return {
      hass: {},
      config: {},
    };
  }

  render() {
    return html`
      <wired-card elevation="2">
        ${this.config.entities.map((ent) => {
          const stateObj = this.hass.states[ent];
          return stateObj
            ? html`
                <div class="state">
                  ${stateObj.attributes.friendly_name}
                  <wired-toggle
                    .checked="${stateObj.state === "on"}"
                    @change="${(ev) => this._toggle(stateObj)}"
                  ></wired-toggle>
                </div>
              `
            : html` <div class="not-found">未找到 Entity ${ent}。</div> `;
        })}
      </wired-card>
    `;
  }

  setConfig(config) {
    if (!config.entities) {
      throw new Error("You need to define entities");
    }
    this.config = config;
  }

  // 你的卡片高度。Home Assistant 使用此值自动
  // 将所有卡片分配到可用的列中。
  getCardSize() {
    return this.config.entities.length + 1;
  }

  _toggle(state) {
    this.hass.callService("homeassistant", "toggle", {
      entity_id: state.entity_id,
    });
  }

  static get styles() {
    return css`
      :host {
        font-family: "Gloria Hallelujah", cursive;
      }
      wired-card {
        background-color: white;
        padding: 16px;
        display: block;
        font-size: 18px;
      }
      .state {
        display: flex;
        justify-content: space-between;
        padding: 8px;
        align-items: center;
      }
      .not-found {
        background-color: yellow;
        font-family: sans-serif;
        font-size: 14px;
        padding: 8px;
      }
      wired-toggle {
        margin-left: 8px;
      }
    `;
  }
}
customElements.define("wired-toggle-card", WiredToggleCard);

在你的 dashboard 配置中添加一个 URL 为 /local/wired-cards.js、类型为 module 的 resource。

以及你的配置:

# 示例 dashboard 配置
views:
  - name: Example
    cards:
      - type: "custom:wired-toggle-card"
        entities:
          - input_boolean.switch_ac_kitchen
          - input_boolean.switch_ac_livingroom
          - input_boolean.switch_tv

图形化卡片配置

你的卡片可以定义一个 getConfigElement 方法,返回一个用于编辑用户配置的 custom element。Home Assistant 将把这个 element 显示在 dashboard 的 card editor 中。

你的卡片还可以定义一个 getStubConfig 方法,以 json 形式返回一个默认的卡片配置(不含 type: 参数),供 dashboard 中的卡片类型选择器使用。

Home Assistant 将在设置时调用 config element 的 setConfig 方法。Home Assistant 会在状态变更时更新 config element 的 hass 属性,以及包含 dashboard 配置信息的 lovelace element。

配置的变更通过派发一个 config-changed 事件传回 dashboard,新配置包含在其 detail 中。

要让你的卡片显示在 dashboard 的卡片选择器对话框中,需要向数组 window.customCards 添加一个描述它的对象。该对象的必填属性为 typename(见下面示例)。

class ContentCardExample extends HTMLElement {
  static getConfigElement() {
    return document.createElement("content-card-editor");
  }

  static getStubConfig() {
    return { entity: "sun.sun" }
  }

  ...
}

customElements.define('content-card-example', ContentCardExample);
class ContentCardEditor extends LitElement {
  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("content-card-editor", ContentCardEditor);
window.customCards = window.customCards || [];
window.customCards.push({
  type: "content-card-example",
  name: "Content Card",
  preview: false, // 可选,默认为 false
  description: "A custom card made by me!", // 可选
  documentationURL:
    "https://developers.home-assistant.io/docs/frontend/custom-ui/custom-card", // 在 frontend 卡片编辑器中添加帮助链接
});

为实体推荐你的卡片

自 Home Assistant 2026.6 起可用。

当用户在卡片选择器中选择一个实体时,Home Assistant 会显示适用于该实体的建议卡片列表。你的卡片可以在其 window.customCards 条目中定义 getEntitySuggestion 函数来加入此列表。建议的 custom cards 会出现在内置建议下方的 Community 部分。

window.customCards.push({
  type: "content-card-example",
  name: "Content Card",
  getEntitySuggestion: (hass, entityId) => {
    // 如果不支持该实体,返回 null
    const domain = entityId.split(".")[0];
    if (domain !== "light") {
      return null;
    }
    // 返回一个建议
    return {
      config: { type: "custom:content-card-example", entity: entityId },
    };
  },
});

getEntitySuggestion(hass, entityId) 会被传入 hass 对象和所选 entity id。它返回:

  • 如果卡片不支持该实体,返回 null
  • 单个建议,或一个建议数组,用于提供多种变体。

仅当你的卡片对该实体确实有意义时才返回建议。使用 hass 对象检查其实体的 domain、device class 或 supported features。为每个实体都推荐你的卡片会让选择器过于杂乱,并导致用户选择错误的卡片。

每个建议都是一个对象,包含:

  • config (必填):用户选择该建议时要应用的卡片配置。必须包含你的 type(带 custom: 前缀)。
  • label (可选):描述变体的简短标签。选择器会先显示卡片的 name,再接这个 label,因此只有返回多个建议时才设置它。
getEntitySuggestion: (hass, entityId) => {
  if (entityId.split(".")[0] !== "light") {
    return null;
  }
  return [
    {
      label: "Compact",
      config: { type: "custom:content-card-example", entity: entityId },
    },
    {
      label: "Detailed",
      config: {
        type: "custom:content-card-example",
        entity: entityId,
        details: true,
      },
    },
  ];
},

使用内置表单编辑器

为图形编辑器配置的一种方式是为卡片提供一个 custom editor element;对于配置需求相对简单的卡片,另一种选择是使用内置的 frontend 表单编辑器。通过在卡片类中定义一个静态 getConfigForm 函数来实现,它返回一个定义配置表单形状的 form schema。

示例:

  static getConfigForm() {
    return {
      schema: [
        { name: "label", selector: { label: {} } },
        { name: "entity", required: true, selector: { entity: {} } },
        {
          type: "grid",
          name: "",
          schema: [
            { name: "name", selector: { text: {} } },
            {
              name: "icon",
              selector: {
                icon: {},
              },
              context: {
                icon_entity: "entity",
              },
            },
            {
              name: "attribute",
              selector: {
                attribute: {},
              },
              context: {
                filter_entity: "entity",
              },
            },
            { name: "unit", selector: { text: {} } },
            { name: "theme", selector: { theme: {} } },
            { name: "state_color", selector: { boolean: {} } },
          ],
        },
      ],
      computeLabel: (schema) => {
        if (schema.name === "icon") return "Special Icon";
        return undefined;
      },
      computeHelper: (schema) => {
        switch (schema.name) {
          case "entity":
            return "This text describes the function of the entity selector";
          case "unit":
            return "The unit of measurement for this card";
        }
        return undefined;
      },
      assertConfig: (config) => {
        if (config.other_option) {
          throw new Error("'other_option' is unexpected.");
        }
      },
    };
  }

从此函数中,应返回一个最多包含 4 个 key 的对象:

  • schema (必填):这是一个 schema 对象列表,每个表单字段对应一个,定义字段的名称、selector 等各种属性。
  • computeLabel (可选):此回调函数会对每个表单字段调用,允许卡片定义要显示的字段 label。如果为 undefined,Home Assistant 可能会为 entity 这类通用字段名应用已知的翻译,或者你也可以提供自己的翻译。
  • computeHelper (可选):此回调函数会对每个表单字段调用,允许你为字段定义更长的 helper 文本,将显示在字段下方。
  • assertConfig (可选):每次配置更新时,用户的配置都会传递给此回调函数。在此回调中抛出 Error 将使可视化编辑器被禁用。这可用于在用户输入不兼容数据时禁用可视化编辑器,比如对期望 string 的 selector 输入了一个 yaml 对象。如果此回调的后续执行未抛出错误,可视化编辑器将被重新启用。

此示例将产生以下配置表单:

配置表单截图

Form schema 元素

form schema 可以包含独立的控件、grid 或 expansion panel,配置选项如下:

Controls:

  • name (必填):控件的名称。
  • selector (可选):此控件的 selector 配置(可用选项请参见 selectors
  • type (可选):如果未定义 selector,有 floatboolean 等原生表单类型,但建议使用 selectors。

Grids:

  • type (必填)grid
  • name (必填):在 form 数据对象中对应此 grid 的 key(参见 flatten
  • schema (必填):grid 中子控件的列表
  • flatten (可选)true/false,指示子控件数据是否应展平到主数据字典中,还是放在以此 grid 命名的子字典下
  • column_min_width (可选):grid 中单元格最小宽度的 CSS 属性(例如 200px

Expansion Panel:

  • type (必填)expandable
  • name (必填):在 form 数据对象中对应此 panel 的 key(参见 flatten
  • schema (必填):expansion panel 中子控件的列表
  • title (可选):panel 上的标题
  • flatten (可选)true/false,指示子控件数据是否应展平到主数据字典中,还是放在以此 panel 命名的子字典下

这不是所有选项的完整列表,更多配置选项列于 ha-form/types.ts