From 4ba48744666210dded87f4903a832de49008f44a Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:32:25 -0700 Subject: [PATCH 1/5] Update custom-card.md --- docs/frontend/custom-ui/custom-card.md | 68 ++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/docs/frontend/custom-ui/custom-card.md b/docs/frontend/custom-ui/custom-card.md index 7e41cdcf242..24937d854bf 100644 --- a/docs/frontend/custom-ui/custom-card.md +++ b/docs/frontend/custom-ui/custom-card.md @@ -319,3 +319,71 @@ window.customCards.push({ "https://developers.home-assistant.io/docs/frontend/custom-ui/custom-card", // Adds a help link in the frontend card editor }); ``` + +### Using the builtin form editor +While one way to configure a graphical editor is to supply a custom editor element, another option for cards with relatively simple configuration requirements is to use the builtin frontend form editor. This is done by defining a static `getConfigForm` function in your card class, that returns a form schema, defining the shape of your configuration form. + +Example: +```js + 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; + }, + }; + } +``` + +The object returned by this function has 3 parts: + +- `schema`: This is a list of schema objects, one per form field, defining various properties of the field, like the name and selector. +- `computeLabel`: This function will be called per form field, allowing the card to define the label that will be displayed for the field. If `undefined`, Home Assistant may apply a known translation for generic field names like `entity`, or you can supply your own translations. +- `computeHelper`: This function will be called per form field, allowing you to define longer helper text for the field, which will be displayed below the field. + +This example then results in the following config form: +![Screenshot of the config form](/img/en/frontend/dashboard-custom-card-config-form.png) + + + From c9e9558970150cde936d0a98adcc53a8f5feaed6 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:40:00 -0700 Subject: [PATCH 2/5] Add files via upload --- .../dashboard-custom-card-config-form.png | Bin 0 -> 18385 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 static/img/en/frontend/dashboard-custom-card-config-form.png diff --git a/static/img/en/frontend/dashboard-custom-card-config-form.png b/static/img/en/frontend/dashboard-custom-card-config-form.png new file mode 100644 index 0000000000000000000000000000000000000000..3f5e883dfc265b21bc74819bef3bae0d36160155 GIT binary patch literal 18385 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 Date: Mon, 11 Aug 2025 08:28:15 -0700 Subject: [PATCH 3/5] Update custom-card.md --- docs/frontend/custom-ui/custom-card.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/frontend/custom-ui/custom-card.md b/docs/frontend/custom-ui/custom-card.md index 24937d854bf..450ab3d98f1 100644 --- a/docs/frontend/custom-ui/custom-card.md +++ b/docs/frontend/custom-ui/custom-card.md @@ -320,8 +320,8 @@ window.customCards.push({ }); ``` -### Using the builtin form editor -While one way to configure a graphical editor is to supply a custom editor element, another option for cards with relatively simple configuration requirements is to use the builtin frontend form editor. This is done by defining a static `getConfigForm` function in your card class, that returns a form schema, defining the shape of your configuration form. +### Using the built-in form editor +While one way to configure a graphical editor is to supply a custom editor element, another option for cards with relatively simple configuration requirements is to use the built-in frontend form editor. This is done by defining a static `getConfigForm` function in your card class, that returns a form schema defining the shape of your configuration form. Example: ```js @@ -380,7 +380,7 @@ The object returned by this function has 3 parts: - `schema`: This is a list of schema objects, one per form field, defining various properties of the field, like the name and selector. - `computeLabel`: This function will be called per form field, allowing the card to define the label that will be displayed for the field. If `undefined`, Home Assistant may apply a known translation for generic field names like `entity`, or you can supply your own translations. -- `computeHelper`: This function will be called per form field, allowing you to define longer helper text for the field, which will be displayed below the field. +- `computeHelper`: This function will be called per form field, allowing you to define longer helper text for the field, which will be displayed below the field. This example then results in the following config form: ![Screenshot of the config form](/img/en/frontend/dashboard-custom-card-config-form.png) From c62adfce81559de072106c935c48113b478cf595 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:45:32 -0700 Subject: [PATCH 4/5] add assertConfig --- docs/frontend/custom-ui/custom-card.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/frontend/custom-ui/custom-card.md b/docs/frontend/custom-ui/custom-card.md index 450ab3d98f1..fe891f71fd7 100644 --- a/docs/frontend/custom-ui/custom-card.md +++ b/docs/frontend/custom-ui/custom-card.md @@ -372,15 +372,20 @@ Example: } return undefined; }, + assertConfig: (config) => { + if (config.other_option) { + throw new Error("'other_option' is unexpected."); + } + }, }; } ``` +From this function, you should return an object with up to 4 keys: -The object returned by this function has 3 parts: - -- `schema`: This is a list of schema objects, one per form field, defining various properties of the field, like the name and selector. -- `computeLabel`: This function will be called per form field, allowing the card to define the label that will be displayed for the field. If `undefined`, Home Assistant may apply a known translation for generic field names like `entity`, or you can supply your own translations. -- `computeHelper`: This function will be called per form field, allowing you to define longer helper text for the field, which will be displayed below the field. +- `schema` _(required)_: This is a list of schema objects, one per form field, defining various properties of the field, like the name and selector. +- `computeLabel` _(optional)_: This callback function will be called per form field, allowing the card to define the label that will be displayed for the field. If `undefined`, Home Assistant may apply a known translation for generic field names like `entity`, or you can supply your own translations. +- `computeHelper` _(optional)_: This callback function will be called per form field, allowing you to define longer helper text for the field, which will be displayed below the field. +- `assertConfig` _(optional)_: On each update of the configuration, the user's config will be passed to this callback function. If you throw an `Error` during this callback, the visual editor will be disabled. This can be used to disable the visual editor when user enters incompatible data, like entering an object in yaml for a selector that expects a string. If a subsequent execution of this callback does not throw an error, the visual editor will be re-enabled. This example then results in the following config form: ![Screenshot of the config form](/img/en/frontend/dashboard-custom-card-config-form.png) From 1b76e6be626da9fc0dbc5ab02c0474d04f652bd4 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Mon, 1 Sep 2025 06:08:54 -0700 Subject: [PATCH 5/5] Update docs/frontend/custom-ui/custom-card.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/frontend/custom-ui/custom-card.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend/custom-ui/custom-card.md b/docs/frontend/custom-ui/custom-card.md index fe891f71fd7..f56a30f101d 100644 --- a/docs/frontend/custom-ui/custom-card.md +++ b/docs/frontend/custom-ui/custom-card.md @@ -385,7 +385,7 @@ From this function, you should return an object with up to 4 keys: - `schema` _(required)_: This is a list of schema objects, one per form field, defining various properties of the field, like the name and selector. - `computeLabel` _(optional)_: This callback function will be called per form field, allowing the card to define the label that will be displayed for the field. If `undefined`, Home Assistant may apply a known translation for generic field names like `entity`, or you can supply your own translations. - `computeHelper` _(optional)_: This callback function will be called per form field, allowing you to define longer helper text for the field, which will be displayed below the field. -- `assertConfig` _(optional)_: On each update of the configuration, the user's config will be passed to this callback function. If you throw an `Error` during this callback, the visual editor will be disabled. This can be used to disable the visual editor when user enters incompatible data, like entering an object in yaml for a selector that expects a string. If a subsequent execution of this callback does not throw an error, the visual editor will be re-enabled. +- `assertConfig` _(optional)_: On each update of the configuration, the user's config will be passed to this callback function. If you throw an `Error` during this callback, the visual editor will be disabled. This can be used to disable the visual editor when the user enters incompatible data, like entering an object in yaml for a selector that expects a string. If a subsequent execution of this callback does not throw an error, the visual editor will be re-enabled. This example then results in the following config form: ![Screenshot of the config form](/img/en/frontend/dashboard-custom-card-config-form.png)