An reimplementation of the original ha-editor-formbuilder (by marcokreeft87)
- with a focus on supporting extensive YAML configurations, controls backed by ha-selector
, conditional expresssion evaluation, and more.
- Card Editor Form Builder for Home Assistant
- Usage
- Installation
- Basic Configuration
- Form Configuration Structure
- Conditions
- Examples
ha-selector
Control Reference- Common Parameters
- Selectors
- String/Text Selector [
type: text
] - Boolean Selector [
type: boolean
] - Number Selector [
type: number
] - Entity Selector [
type: entity
] - Icon Selector [
type: icon
] - Select Selector [
type: select
] - UI Action Selector [
type: ui_action
] - UI Color Selector [
type: ui_color
] - Color Temp Selector [
type: color_temp
] - QR Code Selector [
type: qr_code
]
- String/Text Selector [
This form builder can be integrated into your custom card projects, allowing you to easily create dynamic and interactive forms for your custom card's editor. By leveraging the standard Home Assistant selector controls and dynamic expressions, you can build a clean, adaptable, and user-friendly interface for configuring your cards.
To use the Card Editor Form Builder, first install the module into your project:
npm install 'ha-card-formbuilder@git+https://github.com/snootched/ha-card-formbuilder' --save
You will import the Editor
class into your project and extend it for use.
You can define your form configurations within your code if preferred, but importing from a YAML file provides quick edit capabilities and is the primary use-case.
To extend and implement the Editor class:
-
Import the Editor Class: Import the base Editor class from the module.
-
Extend the Editor Class: Create a new class that extends the base Editor class.
-
Implement Required Methods: Implement the required methods such as
render
. -
Load Configuration for Rendering: Load your form configuration (from YAML or js variable) and call the
generateForm(formContent)
method fromrender()
Here is an example of how to extend and implement the Editor class:
import { Editor } from 'path-to-editor-module';
import { html } from 'lit';
class CustomCardEditor extends Editor {
_formControls;
constructor(cardType) {
super();
// load the configuration from your yaml file with your preferred method of doing so
this._formControls = {};
this._formControls = myYamlLoader('myYamlFile');
}
render() {
if (!this._hass) {
return html`<ha-alert alert-type="error" title="Error">Home Assistant instance is missing.</ha-alert>`;
}
if (!this._config) {
return html`<ha-alert alert-type="error" title="Error">Card configuration is missing.</ha-alert>`;
}
if (!this._formControls) {
return html`<ha-alert alert-type="error" title="Error">Form controls are missing.</ha-alert>`;
}
try {
const formContent = this._formControls;
const returnForm = this.generateForm(formContent);
return returnForm;
} catch (error) {
console.error('Error rendering configuration form:', error);
return html`<ha-alert alert-type="error" title="Error">Error rendering form: ${error.message}</ha-alert>`;
}
}
}
customElements.define('custom-card-editor', CustomCardEditor);
The form configuration is structured in a hierarchical manner using sections, rows, and controls. Each level of the hierarchy has specific properties and types that define the form's layout and behavior. If you have multiple cards to build editors for, you can create an array within your file to do so.
+----------------+
| you-card-# |
+----------------+
|
+-- render_form
| |
| +-- Section
| | |
| | +-- label
| | +-- outlined
| | +-- icon
| | +-- rows
| | |
| | +-- ControlRow
| | | |
| | | +-- cssClass
| | | +-- visibilityCondition
| | | +-- controls
| | | |
| | | +-- Control
| | | | |
| | | | +-- label
| | | | +-- helper
| | | | +-- configValue
| | | | +-- type
| | | | +-- selector
| | | | |
| | | | +-- select
| | | | |
| | | | +-- options
| | | +-- Control |
| | | +-- value
| | | +-- label
| | |
| | +-- ControlRow
| | |
| | +-- cssClass
| | +-- controls
| | |
| | +-- Control
| | |
| | +-- label
| | +-- helper
| | +-- configValue
| | +-- type
| | +-- selector
| | |
| | +-- select
| | |
| | +-- optionsCondition
|
+-- css
|
+-- cssText
A row is a container within a section that holds one or more control rows or sections. Rows can have properties such as visibilityCondition
and cssClass
.
rows:
- type: ControlRow
cssClass: "my-custom-css-class-in-cssText"
controls:
- ....control
- ....control
- type: ControlRow
cssClass: "form-row two-controls"
controls:
- ....control
- ....control
- type: Section
rows:
- type: ControlRow
.
.
A section is a top-level container that groups related rows and controls. It uses ha-expansion-panel
and has properties such as label
, outlined
, icon
, and more.
Tip: Sections can also be nested to form groupings of similar control items into larger control groups.
- type: Section
label: "Section Label"
outlined: true
leftChevron: false
headerLevel: 4
icon: "mdi:icon-name"
noCollapse: false
rows: # Array of rows within the section
- type: ControlRow
cssClass: "form-row"
controls: # Array of controls within the row
- label: "Control Label"
helper: "Helper text"
configValue: "config.path"
type: Selector
selector:
select:
options:
- value: "option1"
label: "Option 1"
- value: "option2"
label: "Option 2"
Parameter | Description | Type | Options | Required | Default Value |
---|---|---|---|---|---|
type |
The type of the element | String | Section |
Yes | - |
label |
The label for the section | String | - | No | - |
outlined |
Whether the section is outlined | Boolean | - | No | true |
leftChevron |
Whether to show a left chevron | Boolean | - | No | false |
headerLevel |
The header level for the section | Number | 1-6 | No | 4 |
icon |
The icon for the section | String | - | No | - |
noCollapse |
Whether the section is collapsible | Boolean | - | No | false |
visibilityCondition |
Condition to determine if the section is visible | String | - | No | - |
rows |
Array of rows (Sections or ControlRows ) |
Array | - | Yes | - |
Control Rows are the rows in the form (or within Sections) that the controls are placed.
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
type |
The type of the element | String | ControlRow |
Yes |
cssClass |
CSS class to apply to the row | String | - | No |
visibilityCondition |
Condition to determine if the section is visible | String | - | No |
controls |
Array of controls within the row | Array | - | Yes |
Controls are the individual form elements within a row. Each control can have various properties such as label
, helper
, configValue
, type
, and more. The type
property determines the specific control type.
The following control types are supported:
Selector
: A dropdown or radio button selector.Divider
: A horizontal divider.Filler
: An empty filler control.Message
: An alert/message control [ha-alert
]RawHTML
: Render a raw HTML string.
- type: Selector
label: "Control Label"
helper: "Helper text"
configValue: "path.to.config.option"
selector:
select:
options:
- value: "option1"
label: "Option 1"
- value: "option2"
label: "Option 2"
Parameter | Description | Type | Options | Default Value | Required |
---|---|---|---|---|---|
type |
The control type | String | Selector Divider Filler Message RawHTML |
- | Yes |
configValue |
The configuration value path the control is for. | String | - | - | Yes |
label |
The label for the control | String | - | - | No |
helper |
Helper text for the control | String | - | - | No |
disabled |
Whether the control is disabled | Boolean | - | false |
No |
required |
Whether the control is required | Boolean | - | false |
No |
selector |
The specific selector definition (see below) | Object | - | - | Yes (type: Selector ) |
visibilityCondition ,disabledCondition |
Conditional expressions (see below) | String | - | - | No |
The visibilityCondition
and disabledCondition
options allow you to dynamically control the visibility and enabled state of controls based on specific conditions. These conditions are evaluated as JavaScript expressions within the context of the form.
You have access to the context including this
, hass
, and window
.
Parameter | Description | Type | Required |
---|---|---|---|
visibilityCondition |
Condition to determine if the control is visible | String | No |
disabledCondition |
Condition to determine if the control is disabled | String | No |
The visibilityCondition
option can be used to show or hide a control based on a specific condition. For example, you can show a control only if a certain configuration value is true.
- type: ControlRow
cssClass: "form-row"
controls:
- label: "Show Icon"
helper: "Toggle to show or hide the icon"
configValue: "show_icon"
type: Switch
visibilityCondition: "this._config.someCondition === true"
In this example, the "Show Icon" control will only be visible if someCondition
is true.
The disabledCondition
option can be used to enable or disable a control based on a specific condition. For example, you can disable a control if a certain configuration value is false.
- type: ControlRow
cssClass: "form-row"
controls:
- label: "Background Color"
helper: "Select the background color"
configValue: "background_color"
type: Selector
selector:
select:
options:
- value: "red"
label: "Red"
- value: "blue"
label: "Blue"
disabledCondition: "this._config.someOtherCondition === false"
In this example, the "Background Color" control will be disabled if someOtherCondition
is false.
You can dynamically generate options arrays for the select
selector based on expressions. For example with access to our context, you can list CSS variables that start with a specific prefix.
- controls:
- label: "Options from Vars"
configValue: "cblcars_card_config.variables.card.background.color.default"
type: Selector
selector:
select:
optionsCondition: |
(() => {
const styles = document.documentElement.style;
const options = [];
for (let i = 0; i < styles.length; i++) {
const name = styles[i];
if (name.startsWith('--picard-')) {
const value = styles.getPropertyValue(name).trim();
options.push({ value, label: name.replace('--', '') });
}
}
return options;
})()
Basic Example Here is a basic example of a form configuration:
my-card:
render_form:
- type: Section
label: "Section 31"
outlined: true
leftChevron: false
headerLevel: 4
icon: "mdi:space-invaders"
noCollapse: false
rows:
- type: ControlRow
cssClass: "form-row two-controls"
controls:
- label: "Label (text)"
helper: "I'm a text selector"
configValue: "configSample.textSelector.typeText"
disabled: false
required: true
type: Selector
selector:
text:
- label: "Label (color)"
helper: "I'm a text color selector"
configValue: "configSample.textSelector.typeColor"
disabled: false
required: true
type: Selector
selector:
color:
my-card:
render_form:
- type: Section
label: "General Settings"
outlined: true
icon: "mdi:settings"
rows:
- type: ControlRow
cssClass: "form-row two-controls"
controls:
- label: "Entity"
helper: "Select the entity"
configValue: "entity"
type: Selector
selector:
entity:
- label: "Show Icon"
helper: "Toggle to show or hide the icon"
configValue: "show_icon"
type: Switch
- type: ControlRow
cssClass: "form-row"
controls:
- label: "Background Color"
helper: "Select the background color"
configValue: "background_color"
type: Selector
selector:
select:
optionsCondition: |
(() => {
const styles = document.documentElement.style;
const options = [];
for (let i = 0; i < styles.length; i++) {
const name = styles[i];
if (name.startsWith('--picard-')) {
const value = styles.getPropertyValue(name).trim();
options.push({ value, label: name.replace('--', '') });
}
}
return options;
})()
Advanced Example Here is an advanced example with dynamic options:
my-card:
render_form:
- type: Section
label: "General Settings"
outlined: true
icon: "mdi:settings"
rows:
- type: ControlRow
cssClass: "form-row two-controls"
controls:
- label: "Entity"
helper: "Select the entity"
configValue: "entity"
type: Selector
selector:
entity:
- label: "Show Icon"
helper: "Toggle to show or hide the icon"
configValue: "show_icon"
type: Switch
- type: ControlRow
cssClass: "form-row"
controls:
- label: "Background Color"
helper: "Select the background color"
configValue: "background_color"
type: Selector
selector:
select:
optionsCondition: |
(() => {
const styles = document.documentElement.style;
const options = [];
for (let i = 0; i < styles.length; i++) {
const name = styles[i];
if (name.startsWith('--picard-')) {
const value = styles.getPropertyValue(name).trim();
options.push({ value, label: name.replace('--', '') });
}
}
return options;
})()
my-card:
render_form:
- type: Section
label: "General Settings"
outlined: true
icon: "mdi:settings"
rows:
- type: ControlRow
cssClass: "form-row two-controls"
controls:
- label: "Entity"
helper: "Select the entity"
configValue: "entity"
type: Selector
selector:
entity:
- label: "Show Icon"
helper: "Toggle to show or hide the icon"
configValue: "show_icon"
type: Selector
selector:
boolean:
- type: ControlRow
cssClass: "form-row"
controls:
- label: "Background Color"
helper: "Select the background color"
configValue: "background_color"
type: Selector
selector:
select:
optionsCondition: |
(() => {
const styles = document.documentElement.style;
const options = [];
for (let i = 0; i < styles.length; i++) {
const name = styles[i];
if (name.startsWith('--picard-')) {
const value = styles.getPropertyValue(name).trim();
options.push({ value, label: name.replace('--', '') });
}
}
return options;
})()
- type: ControlRow
cssClass: "form-row"
controls:
- label: "Show Message"
helper: "Toggle to show or hide the message"
configValue: "show_message"
type: Selector
selector:
boolean:
- type: ControlRow
cssClass: "form-row"
controls:
- type: Message
message: "This is a conditional message."
visibilityCondition: "this._config.show_message === true"
- type: ControlRow
cssClass: "form-row"
controls:
- type: RawHTML
html: "<p>This is a raw HTML block.</p>"
- type: Section
label: "Advanced Settings"
outlined: true
icon: "mdi:settings-outline"
rows:
- type: ControlRow
cssClass: "form-row"
controls:
- label: "Enable Feature"
helper: "Toggle to enable or disable the feature"
configValue: "enable_feature"
type: Selector
selector:
boolean:
- type: ControlRow
cssClass: "form-row"
controls:
- label: "Feature Options"
helper: "Select an option for the feature"
configValue: "feature_option"
type: Selector
selector:
select:
options:
- value: "option1"
label: "Option 1"
- value: "option2"
label: "Option 2"
disabledCondition: "this._config.enable_feature !== true"
- type: Section
label: "Nested Section"
outlined: true
rows:
- type: ControlRow
cssClass: "form-row"
controls:
- label: "Nested Control"
helper: "This is a nested control"
configValue: "nested_control"
type: Selector
selector:
select:
options:
- value: "nested1"
label: "Nested 1"
- value: "nested2"
label: "Nested 2"
The Selector
control type in formbuilder allows users to choose from various ha-selector
definitions.
Below are the common parameters for the control, followed by the specific parameters and options for each selector type.
ha-selector
references can be found in ha-selector.ts
the Home Assistant Front-End GitHub repository.
Interactive demo of most controls can be found in the Front-End Gallery.
Additional Selector documentation can be found in the HA Documentation
Parameter | Description | Type | Default Value | Required |
---|---|---|---|---|
hass |
Home Assistant object | Object | - | Yes |
value |
The current value of the control | Any | - | Yes |
configValue |
The configuration value path | String | - | Yes |
label |
The label for the control | String | - | No |
helper |
Helper text for the control | String | - | No |
disabled |
Whether the control is disabled | Boolean | false |
No |
required |
Whether the control is required | Boolean | false |
No |
selector |
The specific selector definition | Object | - | Yes |
Below is a list of tested selectors that work on card configuraton forms. The full library of selectors is not avaiable as some are meant for blueprint forms.
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
multiline |
Whether the input is multiline | Boolean | - | No |
type |
The type of input | String | number , text , search , tel , url , email , password , date , month , week , time , datetime-local , color |
No |
prefix |
Prefix text for the input | String | - | No |
suffix |
Suffix text for the input | String | - | No |
autocomplete |
Autocomplete attribute for the input | String | - | No |
multiple |
Whether multiple values are allowed | Boolean | - | No |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
boolean |
Boolean value | Object | - | Yes |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
min |
Minimum value | Number | - | No |
max |
Maximum value | Number | - | No |
step |
Step value | Number | any |
No |
mode |
Input mode | String | box , slider |
No |
unit_of_measurement |
Unit of measurement | String | - | No |
slider_ticks |
Whether to show slider ticks | Boolean | - | No |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
multiple |
Whether multiple entities can be selected | Boolean | - | No |
include_entities |
List of entities to include | String[] | - | No |
exclude_entities |
List of entities to exclude | String[] | - | No |
filter |
Filter for entities | Object | See below | No |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
integration |
Integration name | String | - | No |
domain |
Domain name | String | - | No |
device_class |
Device class | String | - | No |
supported_features |
Supported features | Number | - | No |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
placeholder |
Placeholder text for the icon input | String | - | No |
fallbackPath |
Fallback path for the icon | String | - | No |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
multiple |
Whether multiple options can be selected | Boolean | - | No |
custom_value |
Whether custom values are allowed | Boolean | - | No |
mode |
Selection mode | String | list , dropdown |
No |
options |
List of options | String[] | - | Yes |
translation_key |
Translation key for the options | String | - | No |
sort |
Whether to sort the options | Boolean | - | No |
reorder |
Whether to allow reordering of options | Boolean | - | No |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
value |
The value of the option | Any | - | Yes |
label |
The label for the option | String | - | Yes |
disabled |
Whether the option is disabled | Boolean | - | No |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
actions |
List of UI actions | Object[] | - | No |
default_action |
Default UI action | Object | - | No |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
default_color |
Whether to use the default color | Boolean | - | No |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
unit |
Unit of measurement for color temperature | String | kelvin , mired |
No |
min |
Minimum value for color temperature | Number | - | No |
max |
Maximum value for color temperature | Number | - | No |
min_mireds |
Minimum value for mireds | Number | - | No |
max_mireds |
Maximum value for mireds | Number | - | No |
Parameter | Description | Type | Options | Required |
---|---|---|---|---|
data |
The data to encode in the QR code | String | - | Yes |