diff --git a/src/elements/lc-config-card.js b/src/elements/lc-config-card.js
new file mode 100644
index 0000000..752f1f0
--- /dev/null
+++ b/src/elements/lc-config-card.js
@@ -0,0 +1,454 @@
+import { LitElement, html, css, nothing } from 'lit';
+import { hasConfigOrEntityChanged, } from 'custom-card-helpers';
+
+class LandroidConfigCard extends LitElement {
+ static properties = {
+ hass: { type: Object },
+ config: { type: Object },
+ deviceName: { type: String },
+ };
+
+ /**
+ * Lifecycle method to handle the component being connected to the DOM.
+ *
+ * This method is called when the component is connected to the DOM. It is the
+ * opposite of `disconnectedCallback`, and is called when the component is
+ * added to the DOM. This is a good place to set up any initial state or
+ * perform any setup that needs to happen only once.
+ *
+ * @return {void} This function does not return anything.
+ */
+ connectedCallback() {
+ super.connectedCallback();
+ }
+
+ /**
+ * Lifecycle method to handle the component being disconnected from the DOM.
+ *
+ * This method is called when the component is disconnected from the DOM. It
+ * is the opposite of `connectedCallback`, and is called when the component is
+ * removed from the DOM.
+ *
+ * @see https://lit.dev/docs/components/lifecycle/#disconnectedcallback
+ */
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ }
+
+ /**
+ * Lifecycle method that is called after the component has been rendered for the first time.
+ *
+ * This method sets the `_firstRendered` property to `true`, indicating that the component
+ * has completed its first render. It can be used to perform any setup or initialization
+ * that depends on the component being fully rendered.
+ *
+ * @return {void} This function does not return anything.
+ */
+ firstUpdated() {
+ this._firstRendered = true;
+ }
+
+ /**
+ * Lifecycle method to determine if the component should update when its
+ * properties change.
+ *
+ * This method will return true if the component's configuration or any of its
+ * entities have changed. Otherwise, it will return false.
+ *
+ * @param {Object} changedProps - An object with information about which
+ * properties have changed.
+ * @return {boolean} True if the component should update, false otherwise.
+ */
+ shouldUpdate(changedProps) {
+ return hasConfigOrEntityChanged(this, changedProps);
+ }
+
+ /**
+ * Styles for the component.
+ *
+ * The styles are scoped to the component and are used to style the
+ * component's host element. The styles are defined using LitElement's
+ * `css` tag function.
+ *
+ * @return {CSSResult} The styles for the component.
+ */
+ static get styles() {
+ return css`
+ :host {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding: var(--lc-spacing);
+ border-top: 1px solid var(--lc-divider-color);
+ border-bottom: 1px solid var(--lc-divider-color);
+ }
+
+ /* hui-entities-card */
+ #states {
+ flex: 1 1 0%;
+ }
+
+ #states > * {
+ margin: 8px 0px;
+ }
+
+ #states > :first-child {
+ margin-top: 0px;
+ }
+
+ #states > *:last-child {
+ margin-bottom: 0;
+ }
+
+ #states > div > * {
+ overflow: clip visible;
+ }
+
+ #states > div {
+ position: relative;
+ }
+
+ .icon {
+ padding: 0px 18px 0px 8px;
+ }
+
+ /* hui-input-number-entity-row */
+ .flex {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ flex-grow: 2;
+ }
+
+ .state {
+ min-width: 45px;
+ text-align: end;
+ }
+
+ .slider {
+ flex-grow: 2;
+ width: 100px;
+ max-width: 200px;
+ }
+
+ ha-textfield {
+ text-align: end;
+ }
+
+ ha-slider {
+ width: 100%;
+ max-width: 200px;
+ }
+
+ /* hui-input-select-entity-row */
+ ha-select {
+ width: 100%;
+ --ha-select-min-width: 0;
+ }
+ `;
+ }
+
+ /**
+ * Renders the configuration card UI based on the provided entities.
+ *
+ * This function checks if the configuration is valid and iterates over the
+ * entities to render corresponding components based on their domain type,
+ * such as button, number, select, or switch. If the entity's domain is not
+ * recognized, it returns nothing. The rendered entities are wrapped in a
+ * container with the class "entitiescard".
+ *
+ * @return {TemplateResult|nothing} The rendered configuration card UI or
+ * nothing if the configuration or entities are not provided.
+ */
+ render() {
+ if (!this.config || !this.config.entities) return nothing;
+
+ const entities = this.config.entities.map((entityId) => {
+ const domain = entityId.split('.')[0];
+
+ switch (domain) {
+ case 'button':
+ return this.renderButtonEntity(entityId);
+ case 'number':
+ return this.renderNumberEntity(entityId);
+ case 'select':
+ return this.renderSelectEntity(entityId);
+ case 'switch':
+ return this.renderToggleSwitchEntity(entityId);
+ default:
+ return nothing;
+ }
+ });
+
+ return html`
+
+ `;
+ }
+
+/**
+ * Renders a button entity row for the provided entity ID.
+ *
+ * This function retrieves the state object for the given entity ID from Home Assistant,
+ * checks if the entity is available, and constructs a configuration object containing
+ * the entity ID, a friendly name (excluding the device name), and an icon for display.
+ * It returns a lit-html template rendering a generic entity row with a button that,
+ * when clicked, triggers the `pressButton` method for the entity. The button is disabled
+ * if the entity is unavailable.
+ *
+ * @param {string} entityId - The ID of the entity to be rendered.
+ * @return {TemplateResult|nothing} A lit-html template rendering the button entity row,
+ * or `nothing` if the entity is unavailable.
+ */
+ renderButtonEntity(entityId) {
+ const stateObj = this.hass.states[entityId];
+ if (!stateObj || stateObj.state === 'unavailable') return nothing;
+
+ const config = {
+ entity: entityId,
+ name: stateObj.attributes.friendly_name.replace(`${this.deviceName} `, ''),
+ icon: stateObj.attributes.icon,
+ };
+
+ return html`
+
+ this.pressButton(e, entityId)}
+ .disabled=${stateObj.state === 'unavailable'}
+ >
+ ${this.hass.localize('ui.card.button.press')}
+
+
+ `;
+ }
+
+ /**
+ * Renders a number entity row for the provided entity ID.
+ *
+ * This function retrieves the state object for the given entity ID from Home Assistant,
+ * checks if the entity is available, and constructs a configuration object containing
+ * the entity ID, a friendly name (excluding the device name), and an icon for display.
+ * It returns a lit-html template rendering a generic entity row with either a slider
+ * (if the entity is a slider or an auto entity with 256 or fewer steps) or a textfield
+ * (otherwise). The slider or textfield is disabled if the entity is unavailable. The
+ * value of the slider or textfield is the state of the entity, and the unit of measurement
+ * is used as the suffix. When the slider or textfield value changes, the method
+ * `numberValueChanged` is called with the event and the state object as arguments.
+ *
+ * @param {string} entityId - The ID of the entity to be rendered.
+ * @return {TemplateResult|nothing} A lit-html template rendering the number entity row,
+ * or `nothing` if the entity is unavailable.
+ */
+ renderNumberEntity(entityId) {
+ const stateObj = this.hass.states[entityId];
+ if (!stateObj || stateObj.state === 'unavailable') return nothing;
+
+ const config = {
+ entity: entityId,
+ name: stateObj.attributes.friendly_name.replace(`${this.deviceName} `, ''),
+ icon: stateObj.attributes.icon,
+ };
+
+ return html`
+
+ ${stateObj.attributes.mode === 'slider' ||
+ (stateObj.attributes.mode === 'auto' &&
+ (Number(stateObj.attributes.max) - Number(stateObj.attributes.min)) /
+ Number(stateObj.attributes.step) <=
+ 256)
+ ? html`
+ this.numberValueChanged(e, stateObj)}
+ >
+
+ ${this.hass.formatEntityState(stateObj)}
+
+ `
+ : html`
+
+ this.numberValueChanged(e, stateObj)}
+ >
+
+ `}
+
+ `;
+ }
+
+ /**
+ * Renders a select entity element for the given entityId.
+ *
+ * @param {string} entityId - The entityId to render a select element for.
+ * @return {TemplateResult} The rendered element as a TemplateResult.
+ */
+ renderSelectEntity(entityId) {
+ const stateObj = this.hass.states[entityId];
+ if (!stateObj || stateObj.state === 'unavailable') return nothing;
+
+ const config = {
+ entity: entityId,
+ name: stateObj.attributes.friendly_name.replace(`${this.deviceName} `, ''),
+ icon: stateObj.attributes.icon,
+ };
+
+ return html`
+
+ this.selectedChanged(e, stateObj)}
+ @closed=${(e) => e.stopPropagation()}
+ @click=${(e) => e.stopPropagation()}
+ >
+ ${stateObj.attributes.options
+ ? stateObj.attributes.options.map(
+ (option) => html`
+
+ ${this.hass.formatEntityState(stateObj, option)}
+
+ `,
+ )
+ : ''}
+
+
+ `;
+ // ${this.hass.localize(`ui.components.entity.entity-picker.options.${option}`)}
+ }
+
+ /**
+ * Renders a toggle switch for the given entity ID.
+ *
+ * @param {string} entityId - The ID of the entity to be rendered.
+ * @return {TemplateResult} The rendered toggle switch entity row, or `nothing` if the entity is unavailable.
+ */
+ renderToggleSwitchEntity(entityId) {
+ const stateObj = this.hass.states[entityId];
+ if (!stateObj || stateObj.state === 'unavailable') return nothing;
+
+ const config = {
+ entity: entityId,
+ name: stateObj.attributes.friendly_name.replace(`${this.deviceName} `, ''),
+ icon: stateObj.attributes.icon,
+ };
+
+ return html`
+
+ this.toggleChanged(e, stateObj)}
+ >
+
+ `;
+ }
+
+ /**
+ * Triggers a press action for the specified button entity.
+ *
+ * This function stops the event propagation and calls the Home Assistant
+ * service to perform a press action on the button entity identified by
+ * the provided entity ID.
+ *
+ * @param {Event} e - The event object associated with the button press.
+ * @param {string} entity_id - The ID of the button entity to be pressed.
+ */
+ pressButton(e, entity_id) {
+ e.stopPropagation();
+ this.hass.callService("button", "press", {
+ entity_id,
+ });
+ }
+
+ /**
+ * Handles a change in the value of a number input element
+ *
+ * This function is called when the user changes the value of a number input
+ * element associated with a number entity. It checks if the new value is
+ * different from the current state of the entity, and if so, calls the Home
+ * Assistant service to update the state of the entity.
+ *
+ * @param {Event} e - The event object associated with the input change.
+ * @param {Object} stateObj - The entity object with the state to be updated.
+ */
+ numberValueChanged(e, stateObj) {
+ if (e.target.value !== stateObj.state) {
+ this.hass.callService('number', 'set_value', {
+ entity_id: stateObj.entity_id,
+ value: e.target.value,
+ });
+ }
+ }
+
+ /**
+ * Handles a change in the value of a select input element
+ *
+ * This function is called when the user changes the value of a select input
+ * element associated with a select entity. It checks if the new value is
+ * different from the current state of the entity and if the new value is
+ * included in the list of options for the entity. If the new value is valid,
+ * it calls the Home Assistant service to update the state of the entity.
+ *
+ * @param {Event} e - The event object associated with the input change.
+ * @param {Object} stateObj - The entity object with the state to be updated.
+ */
+ selectedChanged(e, stateObj) {
+ const option = e.target.value;
+ if (
+ option === stateObj.state ||
+ !stateObj.attributes.options.includes(option)
+ ) {
+ return;
+ }
+
+ this.hass.callService('select', 'select_option', {
+ entity_id: [stateObj.entity_id],
+ option,
+ });
+ }
+
+ /**
+ * Handles a change in the value of a toggle input element
+ *
+ * This function is called when the user changes the value of a toggle input
+ * element associated with a toggle entity. It checks if the new value is
+ * different from the current state of the entity, and if so, it calls the
+ * Home Assistant service to update the state of the entity.
+ *
+ * @param {Event} e - The event object associated with the input change.
+ * @param {Object} stateObj - The entity object with the state to be updated.
+ */
+ toggleChanged(e, stateObj) {
+ const newState = e.target.checked;
+ if (newState === stateObj.state) return;
+ this.hass.callService('switch', newState ? 'turn_on' : 'turn_off', {
+ entity_id: [stateObj.entity_id],
+ }).then(() => {
+ this.requestUpdate();
+ });
+ }
+
+}
+
+customElements.define('lc-config-card', LandroidConfigCard);
diff --git a/src/landroid-card.js b/src/landroid-card.js
index 56c2995..1dd4a72 100644
--- a/src/landroid-card.js
+++ b/src/landroid-card.js
@@ -11,7 +11,7 @@ import styles from './styles';
import defaultImage from './landroid.svg';
import { version } from '../package.json';
import './landroid-card-editor';
-import { stopPropagation, isObject, wifiStrenghtToQuality } from './helpers';
+import { isObject, wifiStrenghtToQuality } from './helpers';
import * as consts from './constants';
import { DEFAULT_LANG, defaultConfig } from './defaults';
import LandroidCardEditor from './landroid-card-editor';
@@ -804,270 +804,6 @@ return html`
`;
}
- /**
- * Renders the configuration card for the entities specified in the config.
- *
- * @return {TemplateResult} The rendered configuration card as a lit-html TemplateResult or nothing.
- */
- renderConfigCard() {
- if (!this.config || !this.config.settings || !this.showConfigCard) return nothing;
-
- const entities = this.config.settings.map((entityId) => {
- const domain = entityId.split('.')[0];
-
- switch (domain) {
- case 'button':
- return this.renderButtonEntity(entityId);
- case 'number':
- return this.renderNumber(entityId);
- case 'select':
- return this.renderSelectRow(entityId);
- case 'switch':
- return this.renderToggleSwitchEntity(entityId);
- default:
- return nothing;
- }
- });
-
- return html`
-
- `;
-}
-
- /**
- * Presses a button entity when the corresponding button is clicked.
- *
- * @param {Event} e - The click event.
- * @param {string} entity_id - The entity ID of the button entity.
- */
- pressButton(e, entity_id) {
- e.stopPropagation();
- this.hass.callService("button", "press", {
- entity_id,
- });
- }
-
- /**
- * Renders a button for a given entity in the UI.
- *
- * @param {Object} stateObj - The entity object to render.
- * @return {TemplateResult} The rendered button as a TemplateResult.
- */
- renderButtonEntity(entityId) {
- const stateObj = this.getEntityObject(entityId);
- if (!stateObj || stateObj.state === consts.UNAVAILABLE) return nothing;
-
- const config = {
- entity: entityId,
- name: this.getEntityName(stateObj),
- icon: stateObj.attributes.icon,
- };
-
- return html`
-
- this.pressButton(e, config.entity)}
- .disabled=${stateObj.state === consts.UNAVAILABLE}
- >
- ${this.hass.localize("ui.card.button.press")}
-
-
- `;
- }
-
- /**
- * Renders a number input row (Slider or TextField) for an entity card.
- *
- * @param {Object} stateObj - The entity object.
- * @return {TemplateResult} The rendered number input row.
- */
- renderNumber(entityId) {
- const stateObj = this.getEntityObject(entityId);
- if (!stateObj || stateObj.state === consts.UNAVAILABLE) return nothing;
-
- const config = {
- entity: entityId,
- name: this.getEntityName(stateObj),
- icon: stateObj.attributes.icon,
- };
-
- return html`
-
- ${stateObj.attributes.mode === 'slider' ||
- (stateObj.attributes.mode === 'auto' &&
- (Number(stateObj.attributes.max) - Number(stateObj.attributes.min)) /
- Number(stateObj.attributes.step) <=
- 256)
- ? html`
-
- this.numberValueChanged(e, stateObj)}
- >
-
- ${this.hass.formatEntityState(stateObj)}
-
-
- `
- : html`
-
- this.numberValueChanged(e, stateObj)}
- >
-
- `}
-
- `;
- }
-
- /**
- * Handles the change event when a number input value is changed.
- * If the new value is different from the current state of the entity,
- * it calls the 'number.set_value' service with the updated value.
- *
- * @param {Event} e - The event object representing the change event.
- * @param {Object} stateObj - The entity object representing the state.
- * @return {void} This function does not return anything.
- */
- numberValueChanged(e, stateObj) {
- if (e.target.value !== stateObj.state) {
- // this.callService(e, 'number.set_value');
- this.hass.callService('number', 'set_value', {
- entity_id: stateObj.entity_id,
- value: e.target.value,
- });
- }
- }
-
- /**
- * Renders a select row for the given entity state object.
- *
- * @param {Object} stateObj - The entity state object.
- * @return {TemplateResult} The rendered select row.
- */
- renderSelectRow(entityId) {
- const stateObj = this.getEntityObject(entityId);
- if (!stateObj || stateObj.state === consts.UNAVAILABLE) return nothing;
-
- const config = {
- entity: entityId,
- name: this.getEntityName(stateObj),
- icon: stateObj.attributes.icon,
- };
-
- return html`
-
- this.selectedChanged(e, stateObj)}
- @click=${stopPropagation}
- @closed=${stopPropagation}
- >
- ${stateObj.attributes.options
- ? stateObj.attributes.options.map(
- (option) => html`
-
- ${this.hass.formatEntityState(stateObj, option)}
-
- `,
- )
- : ''}
-
-
- `;
- }
-
- /**
- * Handles the change event when a select option is selected.
- *
- * @param {Event} e - The event object representing the change event.
- * @param {Object} stateObj - The entity object representing the state.
- * @return {void} This function does not return anything.
- */
- selectedChanged(e, stateObj) {
- const option = e.target.value;
- if (
- option === stateObj.state ||
- !stateObj.attributes.options.includes(option)
- ) {
- return;
- }
-
- this.hass.callService('select', 'select_option', {
- entity_id: [stateObj.entity_id],
- option,
- });
- }
-
- /**
- * Generates Toggle Switch Entity Row for Entities Card
- * @param {Object} stateObj Entity object
- * @return {TemplateResult} Toggle Switch Entity Row
- */
- renderToggleSwitchEntity(entityId) {
- const stateObj = this.getEntityObject(entityId);
- if (!stateObj || stateObj.state === consts.UNAVAILABLE) return nothing;
-
- const config = {
- entity: entityId,
- name: this.getEntityName(stateObj),
- icon: stateObj.attributes.icon,
- };
-
- return html`
-
- this.toggleChanged(e, stateObj)}
- >
-
- `;
- }
-
- /**
- * Handles the change event when a switch is toggled.
- *
- * @param {Event} e - The event object representing the change event.
- * @param {Object} stateObj - The entity object representing the state.
- * @return {void} This function does not return anything.
- */
- toggleChanged(e, stateObj) {
- const newState = e.target.checked;
- if (newState === stateObj.state) return;
- this.callService(e, `switch.${newState ? 'turn_on' : 'turn_off'}`, {
- entity_id: stateObj.entity_id,
- // isRequest: true,
- });
- // this.hass.callService('switch', newState ? 'turn_on' : 'turn_off', {
- // entity_id: [stateObj.entity_id],
- // }).then(() => {
- // this.requestUpdate();
- // });
- }
-
/**
* Renders the Entities Card for a given card type.
*
@@ -1131,6 +867,55 @@ return html`
`;
}
+ /**
+ * Renders the toolbar component based on the current state.
+ *
+ * @param {string} state - The current state of the component.
+ * @return {TemplateResult} The rendered toolbar component.
+ */
+ renderToolbar(state) {
+ if (!this.showToolbar) {
+ return nothing;
+ }
+
+ const dailyProgress = this.getEntityObject(
+ consts.SENSOR_DAILY_PROGRESS_SUFFIX,
+ );
+
+ return html`
+
+ `;
+ }
+
/**
* Renders the buttons based on the state of the lawn mower.
*
@@ -1196,55 +981,24 @@ return html`
}
/**
- * Renders the toolbar component based on the current state.
- *
- * @param {string} state - The current state of the component.
- * @return {TemplateResult} The rendered toolbar component.
- */
- renderToolbar(state) {
- if (!this.showToolbar) {
- return nothing;
- }
-
- const dailyProgress = this.getEntityObject(
- consts.SENSOR_DAILY_PROGRESS_SUFFIX,
- );
+ * Renders a configuration card to edit the settings.
+ *
+ * The `lc-config-card` element is created and its properties are set based on the
+ * component's configuration and device name.
+ *
+ * @return {HTMLElement} The rendered configuration card element.
+ */
+ renderConfigCard() {
+ if (!this.config || !this.config.settings || !this.showConfigCard) return nothing;
- return html`
-
- `;
+ const configCard = document.createElement('lc-config-card');
+ configCard.hass = this.hass;
+ configCard.config = { entities: this.config.settings, };
+ configCard.deviceName = this.getAttributes().friendly_name;
+ return configCard;
}
- /**
+ /**
* Renders the HTML template for the component.
*
* @return {TemplateResult} The rendered HTML template.