mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 15:26:36 +00:00
Add Text entity (#14447)
This commit is contained in:
parent
0aa2c9044a
commit
a9d44fcb61
@ -138,7 +138,7 @@ if (!window.cardTools) {
|
|||||||
return cardTools.createThing("row", config);
|
return cardTools.createThing("row", config);
|
||||||
|
|
||||||
const domain = config.entity.split(".", 1)[0];
|
const domain = config.entity.split(".", 1)[0];
|
||||||
Object.assign(config, { type: DEFAULT_ROWS[domain] || "text" });
|
Object.assign(config, { type: DEFAULT_ROWS[domain] || "simple" });
|
||||||
return cardTools.createThing("entity-row", config);
|
return cardTools.createThing("entity-row", config);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -98,6 +98,9 @@ const ENTITIES = [
|
|||||||
minimum: 0,
|
minimum: 0,
|
||||||
maximum: 10,
|
maximum: 10,
|
||||||
}),
|
}),
|
||||||
|
getEntity("text", "message", "Hello!", {
|
||||||
|
friendly_name: "Message",
|
||||||
|
}),
|
||||||
|
|
||||||
getEntity("light", "unavailable", "unavailable", {
|
getEntity("light", "unavailable", "unavailable", {
|
||||||
friendly_name: "Bed Light",
|
friendly_name: "Bed Light",
|
||||||
@ -129,6 +132,9 @@ const ENTITIES = [
|
|||||||
friendly_name: "Who cooks",
|
friendly_name: "Who cooks",
|
||||||
icon: "mdi:cheff",
|
icon: "mdi:cheff",
|
||||||
}),
|
}),
|
||||||
|
getEntity("text", "unavailable", "unavailable", {
|
||||||
|
friendly_name: "Message",
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONFIGS = [
|
const CONFIGS = [
|
||||||
@ -147,6 +153,7 @@ const CONFIGS = [
|
|||||||
- climate.ecobee
|
- climate.ecobee
|
||||||
- input_number.number
|
- input_number.number
|
||||||
- sensor.humidity
|
- sensor.humidity
|
||||||
|
- text.message
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -219,6 +226,7 @@ const CONFIGS = [
|
|||||||
- climate.unavailable
|
- climate.unavailable
|
||||||
- input_number.unavailable
|
- input_number.unavailable
|
||||||
- input_select.unavailable
|
- input_select.unavailable
|
||||||
|
- text.unavailable
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -114,6 +114,7 @@ export const FIXED_DOMAIN_ICONS = {
|
|||||||
siren: mdiBullhorn,
|
siren: mdiBullhorn,
|
||||||
simple_alarm: mdiBell,
|
simple_alarm: mdiBell,
|
||||||
sun: mdiWhiteBalanceSunny,
|
sun: mdiWhiteBalanceSunny,
|
||||||
|
text: mdiFormTextbox,
|
||||||
timer: mdiTimerOutline,
|
timer: mdiTimerOutline,
|
||||||
updater: mdiCloudUpload,
|
updater: mdiCloudUpload,
|
||||||
vacuum: mdiRobotVacuum,
|
vacuum: mdiRobotVacuum,
|
||||||
@ -182,6 +183,7 @@ export const DOMAINS_WITH_CARD = [
|
|||||||
"script",
|
"script",
|
||||||
"select",
|
"select",
|
||||||
"timer",
|
"timer",
|
||||||
|
"text",
|
||||||
"vacuum",
|
"vacuum",
|
||||||
"water_heater",
|
"water_heater",
|
||||||
];
|
];
|
||||||
@ -214,6 +216,7 @@ export const DOMAINS_INPUT_ROW = [
|
|||||||
"script",
|
"script",
|
||||||
"select",
|
"select",
|
||||||
"switch",
|
"switch",
|
||||||
|
"text",
|
||||||
"vacuum",
|
"vacuum",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
19
src/data/text.ts
Normal file
19
src/data/text.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import {
|
||||||
|
HassEntityAttributeBase,
|
||||||
|
HassEntityBase,
|
||||||
|
} from "home-assistant-js-websocket";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
interface TextEntityAttributes extends HassEntityAttributeBase {
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
pattern?: string;
|
||||||
|
mode?: "text" | "password";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TextEntity extends HassEntityBase {
|
||||||
|
attributes: TextEntityAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setValue = (hass: HomeAssistant, entity: string, value: string) =>
|
||||||
|
hass.callService("text", "set_value", { value }, { entity_id: entity });
|
@ -50,8 +50,9 @@ export const DOMAINS_HIDE_DEFAULT_MORE_INFO = [
|
|||||||
"input_text",
|
"input_text",
|
||||||
"number",
|
"number",
|
||||||
"scene",
|
"scene",
|
||||||
"update",
|
|
||||||
"select",
|
"select",
|
||||||
|
"text",
|
||||||
|
"update",
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Domains that should have the history hidden in the more info dialog. */
|
/** Domains that should have the history hidden in the more info dialog. */
|
||||||
|
@ -2,7 +2,7 @@ import "../entity-rows/hui-media-player-entity-row";
|
|||||||
import "../entity-rows/hui-scene-entity-row";
|
import "../entity-rows/hui-scene-entity-row";
|
||||||
import "../entity-rows/hui-script-entity-row";
|
import "../entity-rows/hui-script-entity-row";
|
||||||
import "../entity-rows/hui-sensor-entity-row";
|
import "../entity-rows/hui-sensor-entity-row";
|
||||||
import "../entity-rows/hui-text-entity-row";
|
import "../entity-rows/hui-simple-entity-row";
|
||||||
import "../entity-rows/hui-toggle-entity-row";
|
import "../entity-rows/hui-toggle-entity-row";
|
||||||
import { LovelaceRowConfig } from "../entity-rows/types";
|
import { LovelaceRowConfig } from "../entity-rows/types";
|
||||||
import "../special-rows/hui-attribute-row";
|
import "../special-rows/hui-attribute-row";
|
||||||
@ -18,7 +18,7 @@ const ALWAYS_LOADED_TYPES = new Set([
|
|||||||
"scene-entity",
|
"scene-entity",
|
||||||
"script-entity",
|
"script-entity",
|
||||||
"sensor-entity",
|
"sensor-entity",
|
||||||
"text-entity",
|
"simple-entity",
|
||||||
"toggle-entity",
|
"toggle-entity",
|
||||||
"button",
|
"button",
|
||||||
"call-service",
|
"call-service",
|
||||||
@ -41,6 +41,7 @@ const LAZY_LOAD_TYPES = {
|
|||||||
"lock-entity": () => import("../entity-rows/hui-lock-entity-row"),
|
"lock-entity": () => import("../entity-rows/hui-lock-entity-row"),
|
||||||
"number-entity": () => import("../entity-rows/hui-number-entity-row"),
|
"number-entity": () => import("../entity-rows/hui-number-entity-row"),
|
||||||
"select-entity": () => import("../entity-rows/hui-select-entity-row"),
|
"select-entity": () => import("../entity-rows/hui-select-entity-row"),
|
||||||
|
"text-entity": () => import("../entity-rows/hui-text-entity-row"),
|
||||||
"timer-entity": () => import("../entity-rows/hui-timer-entity-row"),
|
"timer-entity": () => import("../entity-rows/hui-timer-entity-row"),
|
||||||
conditional: () => import("../special-rows/hui-conditional-row"),
|
conditional: () => import("../special-rows/hui-conditional-row"),
|
||||||
"weather-entity": () => import("../entity-rows/hui-weather-entity-row"),
|
"weather-entity": () => import("../entity-rows/hui-weather-entity-row"),
|
||||||
@ -53,7 +54,7 @@ const LAZY_LOAD_TYPES = {
|
|||||||
text: () => import("../special-rows/hui-text-row"),
|
text: () => import("../special-rows/hui-text-row"),
|
||||||
};
|
};
|
||||||
const DOMAIN_TO_ELEMENT_TYPE = {
|
const DOMAIN_TO_ELEMENT_TYPE = {
|
||||||
_domain_not_found: "text",
|
_domain_not_found: "simple",
|
||||||
alert: "toggle",
|
alert: "toggle",
|
||||||
automation: "toggle",
|
automation: "toggle",
|
||||||
button: "button",
|
button: "button",
|
||||||
@ -78,6 +79,7 @@ const DOMAIN_TO_ELEMENT_TYPE = {
|
|||||||
sensor: "sensor",
|
sensor: "sensor",
|
||||||
siren: "toggle",
|
siren: "toggle",
|
||||||
switch: "toggle",
|
switch: "toggle",
|
||||||
|
text: "text",
|
||||||
timer: "timer",
|
timer: "timer",
|
||||||
vacuum: "toggle",
|
vacuum: "toggle",
|
||||||
// Temporary. Once climate is rewritten,
|
// Temporary. Once climate is rewritten,
|
||||||
|
73
src/panels/lovelace/entity-rows/hui-simple-entity-row.ts
Normal file
73
src/panels/lovelace/entity-rows/hui-simple-entity-row.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { EntitiesCardEntityConfig } from "../cards/types";
|
||||||
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
|
import "../components/hui-generic-entity-row";
|
||||||
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
|
import { LovelaceRow } from "./types";
|
||||||
|
|
||||||
|
@customElement("hui-simple-entity-row")
|
||||||
|
class HuiSimpleEntityRow extends LitElement implements LovelaceRow {
|
||||||
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _config?: EntitiesCardEntityConfig;
|
||||||
|
|
||||||
|
public setConfig(config: EntitiesCardEntityConfig): void {
|
||||||
|
if (!config) {
|
||||||
|
throw new Error("Invalid configuration");
|
||||||
|
}
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
|
return hasConfigOrEntityChanged(this, changedProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this._config || !this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateObj = this.hass.states[this._config.entity];
|
||||||
|
|
||||||
|
if (!stateObj) {
|
||||||
|
return html`
|
||||||
|
<hui-warning>
|
||||||
|
${createEntityNotFoundWarning(this.hass, this._config.entity)}
|
||||||
|
</hui-warning>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
|
||||||
|
${computeStateDisplay(this.hass!.localize, stateObj, this.hass.locale)}
|
||||||
|
</hui-generic-entity-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
div {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-simple-entity-row": HuiSimpleEntityRow;
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,22 @@
|
|||||||
import {
|
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
PropertyValues,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";
|
||||||
|
import { TextEntity, setValue } from "../../../data/text";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { EntitiesCardEntityConfig } from "../cards/types";
|
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
import { LovelaceRow } from "./types";
|
import { EntityConfig, LovelaceRow } from "./types";
|
||||||
|
import "../../../components/ha-textfield";
|
||||||
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
|
|
||||||
@customElement("hui-text-entity-row")
|
@customElement("hui-text-entity-row")
|
||||||
class HuiTextEntityRow extends LitElement implements LovelaceRow {
|
class HuiTextEntityRow extends LitElement implements LovelaceRow {
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@state() private _config?: EntitiesCardEntityConfig;
|
@state() private _config?: EntityConfig;
|
||||||
|
|
||||||
public setConfig(config: EntitiesCardEntityConfig): void {
|
public setConfig(config: EntityConfig): void {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw new Error("Invalid configuration");
|
throw new Error("Invalid configuration");
|
||||||
}
|
}
|
||||||
@ -37,7 +32,9 @@ class HuiTextEntityRow extends LitElement implements LovelaceRow {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateObj = this.hass.states[this._config.entity];
|
const stateObj = this.hass.states[this._config.entity] as
|
||||||
|
| TextEntity
|
||||||
|
| undefined;
|
||||||
|
|
||||||
if (!stateObj) {
|
if (!stateObj) {
|
||||||
return html`
|
return html`
|
||||||
@ -48,23 +45,54 @@ class HuiTextEntityRow extends LitElement implements LovelaceRow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
|
<hui-generic-entity-row
|
||||||
${computeStateDisplay(this.hass!.localize, stateObj, this.hass.locale)}
|
.hass=${this.hass}
|
||||||
|
.config=${this._config}
|
||||||
|
hideName
|
||||||
|
>
|
||||||
|
<ha-textfield
|
||||||
|
.label=${this._config.name || computeStateName(stateObj)}
|
||||||
|
.disabled=${stateObj.state === UNAVAILABLE}
|
||||||
|
.value=${stateObj.state}
|
||||||
|
.minlength=${stateObj.attributes.min}
|
||||||
|
.maxlength=${stateObj.attributes.max}
|
||||||
|
.autoValidate=${stateObj.attributes.pattern}
|
||||||
|
.pattern=${stateObj.attributes.pattern}
|
||||||
|
.type=${stateObj.attributes.mode}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
placeholder=${this.hass!.localize("ui.card.text.emtpy_value")}
|
||||||
|
></ha-textfield>
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
private _valueChanged(ev): void {
|
||||||
return css`
|
const stateObj = this.hass!.states[this._config!.entity] as TextEntity;
|
||||||
div {
|
const newValue = ev.target.value;
|
||||||
text-align: right;
|
|
||||||
|
// Filter out invalid text states
|
||||||
|
if (newValue && UNAVAILABLE_STATES.includes(newValue)) {
|
||||||
|
ev.target.value = stateObj.state;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
.pointer {
|
|
||||||
cursor: pointer;
|
if (newValue !== stateObj.state) {
|
||||||
|
setValue(this.hass!, stateObj.entity_id, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.target.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
hui-generic-entity-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
ha-textfield {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
|
@ -17,6 +17,7 @@ import "./state-card-number";
|
|||||||
import "./state-card-scene";
|
import "./state-card-scene";
|
||||||
import "./state-card-script";
|
import "./state-card-script";
|
||||||
import "./state-card-select";
|
import "./state-card-select";
|
||||||
|
import "./state-card-text";
|
||||||
import "./state-card-timer";
|
import "./state-card-timer";
|
||||||
import "./state-card-toggle";
|
import "./state-card-toggle";
|
||||||
import "./state-card-vacuum";
|
import "./state-card-vacuum";
|
||||||
|
73
src/state-summary/state-card-text.ts
Normal file
73
src/state-summary/state-card-text.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import "../components/ha-textfield";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
|
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||||
|
import "../components/entity/state-badge";
|
||||||
|
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../data/entity";
|
||||||
|
import { TextEntity, setValue } from "../data/text";
|
||||||
|
import type { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
@customElement("state-card-text")
|
||||||
|
class StateCardText extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public stateObj!: TextEntity;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<state-badge .stateObj=${this.stateObj}></state-badge>
|
||||||
|
<ha-textfield
|
||||||
|
.label=${computeStateName(this.stateObj)}
|
||||||
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
|
.value=${this.stateObj.state}
|
||||||
|
.minlength=${this.stateObj.attributes.min}
|
||||||
|
.maxlength=${this.stateObj.attributes.max}
|
||||||
|
.autoValidate=${this.stateObj.attributes.pattern}
|
||||||
|
.pattern=${this.stateObj.attributes.pattern}
|
||||||
|
.type=${this.stateObj.attributes.mode}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
@click=${stopPropagation}
|
||||||
|
placeholder=${this.hass.localize("ui.card.text.emtpy_value")}
|
||||||
|
></ha-textfield>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev): void {
|
||||||
|
const value = ev.target.value;
|
||||||
|
|
||||||
|
// Filter out invalid text states
|
||||||
|
if (value && UNAVAILABLE_STATES.includes(value)) {
|
||||||
|
ev.target.value = this.stateObj.state;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === this.stateObj.state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setValue(this.hass!, this.stateObj.entity_id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
state-badge {
|
||||||
|
float: left;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-textfield {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-text": StateCardText;
|
||||||
|
}
|
||||||
|
}
|
@ -235,6 +235,9 @@
|
|||||||
"installing_with_progress": "Installing ({progress}%)",
|
"installing_with_progress": "Installing ({progress}%)",
|
||||||
"up_to_date": "Up-to-date"
|
"up_to_date": "Up-to-date"
|
||||||
},
|
},
|
||||||
|
"text": {
|
||||||
|
"emtpy_value": "(empty value)"
|
||||||
|
},
|
||||||
"timer": {
|
"timer": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"start": "start",
|
"start": "start",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user