Compare commits

..

13 Commits

Author SHA1 Message Date
Zack Barett
2a12172eeb Bumped version to 20220329.0 (#12152) 2022-03-29 22:25:56 +00:00
Joakim Sørensen
85d3011625 Add badge to configuration sidebar to indicate pending updates (#12146) 2022-03-29 07:35:05 -05:00
Zack Barett
ca22ec6340 Add selector initial values (#12142) 2022-03-28 10:38:58 -07:00
Zack Barett
61f6e8855b Allow binary sensor device class updates (#12124) 2022-03-28 10:44:21 -05:00
Bram Kragten
a44b8981e1 break theme picker out of lovelace (#12140) 2022-03-28 08:21:16 -07:00
Zack Barett
b080bca9ce Add Area Multiple Selector option (#12138) 2022-03-28 09:07:00 -05:00
Bram Kragten
d30e8ee9d8 Make padding on settings row content consistent (#12139) 2022-03-28 08:50:07 -05:00
Bram Kragten
637e4203e5 Fix z-index map, always set icon for location selector (#12137) 2022-03-28 13:14:24 +00:00
Zack Barett
2648a53bbc Merge pull request #12135 from home-assistant/update-automation-type 2022-03-28 07:43:21 -05:00
Paulus Schoutsen
b3fa0cccb4 Add variables to automation trigger type 2022-03-27 20:33:22 -07:00
Zack Barett
dd963be723 Add Day to duration selector (#12125) 2022-03-24 17:57:20 -07:00
Paulus Schoutsen
224df896a1 Allow rendering helper text from strings.json (#12119)
* Allow rendering helper text from strings.json

* Persistent helpers

* Update src/components/ha-base-time-input.ts

Co-authored-by: Zack Barett <zackbarett@hey.com>

* Update src/components/ha-base-time-input.ts

Co-authored-by: Zack Barett <zackbarett@hey.com>
2022-03-24 17:50:38 -07:00
Pawel
a58b4fb262 Fix possibility to enable entity disabled by integration (#12121)
Co-authored-by: Zack Barett <zackbarett@hey.com>
2022-03-24 20:10:49 +01:00
54 changed files with 847 additions and 229 deletions

View File

@@ -1,18 +1,18 @@
/* eslint-disable lit/no-template-arrow */ /* eslint-disable lit/no-template-arrow */
import "@material/mwc-button"; import "@material/mwc-button";
import { LitElement, TemplateResult, html } from "lit"; import { html, LitElement, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators"; import { customElement, state } from "lit/decorators";
import { computeInitialHaFormData } from "../../../../src/components/ha-form/compute-initial-ha-form-data";
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
import "../../../../src/components/ha-form/ha-form";
import "../../components/demo-black-white-row";
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry"; import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry"; import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry"; import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor"; import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import { computeInitialHaFormData } from "../../../../src/components/ha-form/compute-initial-ha-form-data";
import "../../../../src/components/ha-form/ha-form";
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass"; import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { getEntity } from "../../../../src/fake_data/entity"; import "../../components/demo-black-white-row";
const ENTITIES = [ const ENTITIES = [
getEntity("alarm_control_panel", "alarm", "disarmed", { getEntity("alarm_control_panel", "alarm", "disarmed", {
@@ -147,7 +147,9 @@ const SCHEMAS: {
{ name: "target", selector: { target: {} } }, { name: "target", selector: { target: {} } },
{ name: "number", selector: { number: { min: 0, max: 10 } } }, { name: "number", selector: { number: { min: 0, max: 10 } } },
{ name: "boolean", selector: { boolean: {} } }, { name: "boolean", selector: { boolean: {} } },
{ name: "time", selector: { time: {} } }, { name: "time", required: true, selector: { time: {} } },
{ name: "datetime", required: true, selector: { datetime: {} } },
{ name: "date", required: true, selector: { date: {} } },
{ name: "action", selector: { action: {} } }, { name: "action", selector: { action: {} } },
{ name: "text", selector: { text: { multiline: false } } }, { name: "text", selector: { text: { multiline: false } } },
{ name: "text_multiline", selector: { text: { multiline: true } } }, { name: "text_multiline", selector: { text: { multiline: true } } },

View File

@@ -1,20 +1,20 @@
/* eslint-disable lit/no-template-arrow */ /* eslint-disable lit/no-template-arrow */
import "@material/mwc-button"; import "@material/mwc-button";
import { LitElement, TemplateResult, css, html } from "lit"; import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators"; import { customElement, state } from "lit/decorators";
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import "../../../../src/components/ha-selector/ha-selector"; import "../../../../src/components/ha-selector/ha-selector";
import "../../../../src/components/ha-settings-row"; import "../../../../src/components/ha-settings-row";
import { BlueprintInput } from "../../../../src/data/blueprint";
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass"; import { provideHass } from "../../../../src/fake_data/provide_hass";
import { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin";
import type { HomeAssistant } from "../../../../src/types"; import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row"; import "../../components/demo-black-white-row";
import { BlueprintInput } from "../../../../src/data/blueprint";
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import { getEntity } from "../../../../src/fake_data/entity";
import { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin";
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
const ENTITIES = [ const ENTITIES = [
getEntity("alarm_control_panel", "alarm", "disarmed", { getEntity("alarm_control_panel", "alarm", "disarmed", {
@@ -202,6 +202,7 @@ const SCHEMAS: {
input: { input: {
entity: { name: "Entity", selector: { entity: { multiple: true } } }, entity: { name: "Entity", selector: { entity: { multiple: true } } },
device: { name: "Device", selector: { device: { multiple: true } } }, device: { name: "Device", selector: { device: { multiple: true } } },
area: { name: "Area", selector: { area: { multiple: true } } },
}, },
}, },
]; ];

View File

@@ -1,6 +1,6 @@
[metadata] [metadata]
name = home-assistant-frontend name = home-assistant-frontend
version = 20220322.0 version = 20220329.0
author = The Home Assistant Authors author = The Home Assistant Authors
author_email = hello@home-assistant.io author_email = hello@home-assistant.io
license = Apache-2.0 license = Apache-2.0

View File

@@ -18,7 +18,6 @@ import "./state-badge";
interface HassEntityWithCachedName extends HassEntity { interface HassEntityWithCachedName extends HassEntity {
friendly_name: string; friendly_name: string;
id: string;
} }
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean; export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
@@ -97,9 +96,6 @@ export class HaEntityPicker extends LitElement {
@property() public entityFilter?: HaEntityPickerEntityFilterFunc; @property() public entityFilter?: HaEntityPickerEntityFilterFunc;
@property({ attribute: "item-value-path" }) public itemValuePath =
"entity_id";
@property({ type: Boolean }) public hideClearIcon = false; @property({ type: Boolean }) public hideClearIcon = false;
@state() private _opened = false; @state() private _opened = false;
@@ -148,7 +144,6 @@ export class HaEntityPicker extends LitElement {
state: "", state: "",
last_changed: "", last_changed: "",
last_updated: "", last_updated: "",
id: "",
context: { id: "", user_id: null, parent_id: null }, context: { id: "", user_id: null, parent_id: null },
friendly_name: this.hass!.localize( friendly_name: this.hass!.localize(
"ui.components.entity.entity-picker.no_entities" "ui.components.entity.entity-picker.no_entities"
@@ -169,15 +164,10 @@ export class HaEntityPicker extends LitElement {
); );
return entityIds return entityIds
.map((key) => { .map((key) => ({
const stateObj = hass!.states[key]; ...hass!.states[key],
friendly_name: computeStateName(hass!.states[key]) || key,
return { }))
...stateObj,
friendly_name: computeStateName(stateObj) || key,
id: stateObj.context.id,
};
})
.sort((entityA, entityB) => .sort((entityA, entityB) =>
caseInsensitiveStringCompare( caseInsensitiveStringCompare(
entityA.friendly_name, entityA.friendly_name,
@@ -205,15 +195,10 @@ export class HaEntityPicker extends LitElement {
} }
states = entityIds states = entityIds
.map((key) => { .map((key) => ({
const stateObj = hass!.states[key]; ...hass!.states[key],
friendly_name: computeStateName(hass!.states[key]) || key,
return { }))
...stateObj,
friendly_name: computeStateName(stateObj) || key,
id: stateObj.context?.id,
};
})
.sort((entityA, entityB) => .sort((entityA, entityB) =>
caseInsensitiveStringCompare( caseInsensitiveStringCompare(
entityA.friendly_name, entityA.friendly_name,
@@ -258,7 +243,6 @@ export class HaEntityPicker extends LitElement {
state: "", state: "",
last_changed: "", last_changed: "",
last_updated: "", last_updated: "",
id: "",
context: { id: "", user_id: null, parent_id: null }, context: { id: "", user_id: null, parent_id: null },
friendly_name: this.hass!.localize( friendly_name: this.hass!.localize(
"ui.components.entity.entity-picker.no_match" "ui.components.entity.entity-picker.no_match"
@@ -311,8 +295,8 @@ export class HaEntityPicker extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<ha-combo-box <ha-combo-box
item-value-path="entity_id"
item-label-path="friendly_name" item-label-path="friendly_name"
.itemValuePath=${this.itemValuePath}
.hass=${this.hass} .hass=${this.hass}
.value=${this._value} .value=${this._value}
.label=${this.label === undefined .label=${this.label === undefined

View File

@@ -0,0 +1,160 @@
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import type { EntityRegistryEntry } from "../data/entity_registry";
import { SubscribeMixin } from "../mixins/subscribe-mixin";
import type { HomeAssistant } from "../types";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
import "./ha-area-picker";
@customElement("ha-areas-picker")
export class HaAreasPicker extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public label?: string;
@property() public value?: string[];
@property() public placeholder?: string;
@property({ type: Boolean, attribute: "no-add" })
public noAdd?: boolean;
/**
* Show only areas with entities from specific domains.
* @type {Array}
* @attr include-domains
*/
@property({ type: Array, attribute: "include-domains" })
public includeDomains?: string[];
/**
* Show no areas with entities of these domains.
* @type {Array}
* @attr exclude-domains
*/
@property({ type: Array, attribute: "exclude-domains" })
public excludeDomains?: string[];
/**
* Show only areas with entities of these device classes.
* @type {Array}
* @attr include-device-classes
*/
@property({ type: Array, attribute: "include-device-classes" })
public includeDeviceClasses?: string[];
@property() public deviceFilter?: HaDevicePickerDeviceFilterFunc;
@property() public entityFilter?: (entity: EntityRegistryEntry) => boolean;
@property({ attribute: "picked-area-label" })
public pickedAreaLabel?: string;
@property({ attribute: "pick-area-label" })
public pickAreaLabel?: string;
@property({ type: Boolean }) public disabled?: boolean;
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
const currentAreas = this._currentAreas;
return html`
${currentAreas.map(
(area) => html`
<div>
<ha-area-picker
.curValue=${area}
.noAdd=${this.noAdd}
.hass=${this.hass}
.value=${area}
.label=${this.pickedAreaLabel}
.includeDomains=${this.includeDomains}
.excludeDomains=${this.excludeDomains}
.includeDeviceClasses=${this.includeDeviceClasses}
.deviceFilter=${this.deviceFilter}
.entityFilter=${this.entityFilter}
.disabled=${this.disabled}
@value-changed=${this._areaChanged}
></ha-area-picker>
</div>
`
)}
<div>
<ha-area-picker
.noAdd=${this.noAdd}
.hass=${this.hass}
.label=${this.pickAreaLabel}
.includeDomains=${this.includeDomains}
.excludeDomains=${this.excludeDomains}
.includeDeviceClasses=${this.includeDeviceClasses}
.deviceFilter=${this.deviceFilter}
.entityFilter=${this.entityFilter}
.disabled=${this.disabled}
.placeholder=${this.placeholder}
@value-changed=${this._addArea}
></ha-area-picker>
</div>
`;
}
private get _currentAreas(): string[] {
return this.value || [];
}
private async _updateAreas(areas) {
this.value = areas;
fireEvent(this, "value-changed", {
value: areas,
});
}
private _areaChanged(ev: CustomEvent) {
ev.stopPropagation();
const curValue = (ev.currentTarget as any).curValue;
const newValue = ev.detail.value;
if (newValue === curValue) {
return;
}
const currentAreas = this._currentAreas;
if (!newValue || currentAreas.includes(newValue)) {
this._updateAreas(currentAreas.filter((ent) => ent !== curValue));
return;
}
this._updateAreas(
currentAreas.map((ent) => (ent === curValue ? newValue : ent))
);
}
private _addArea(ev: CustomEvent) {
ev.stopPropagation();
const toAdd = ev.detail.value;
if (!toAdd) {
return;
}
(ev.currentTarget as any).value = "";
const currentAreas = this._currentAreas;
if (currentAreas.includes(toAdd)) {
return;
}
this._updateAreas([...currentAreas, toAdd]);
}
static override styles = css`
div {
margin-top: 8px;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-areas-picker": HaAreasPicker;
}
}

View File

@@ -1,12 +1,13 @@
import { LitElement, html, TemplateResult, css } from "lit";
import { customElement, property } from "lit/decorators";
import "./ha-select";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import "./ha-textfield"; import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation"; import { stopPropagation } from "../common/dom/stop_propagation";
import "./ha-select";
import "./ha-textfield";
export interface TimeChangedEvent { export interface TimeChangedEvent {
days?: number;
hours: number; hours: number;
minutes: number; minutes: number;
seconds: number; seconds: number;
@@ -21,6 +22,11 @@ export class HaBaseTimeInput extends LitElement {
*/ */
@property() label?: string; @property() label?: string;
/**
* Helper for the input
*/
@property() helper?: string;
/** /**
* auto validate time inputs * auto validate time inputs
*/ */
@@ -41,6 +47,11 @@ export class HaBaseTimeInput extends LitElement {
*/ */
@property({ type: Boolean }) disabled = false; @property({ type: Boolean }) disabled = false;
/**
* day
*/
@property({ type: Number }) days = 0;
/** /**
* hour * hour
*/ */
@@ -61,6 +72,11 @@ export class HaBaseTimeInput extends LitElement {
*/ */
@property({ type: Number }) milliseconds = 0; @property({ type: Number }) milliseconds = 0;
/**
* Label for the day input
*/
@property() dayLabel = "";
/** /**
* Label for the hour input * Label for the hour input
*/ */
@@ -91,6 +107,11 @@ export class HaBaseTimeInput extends LitElement {
*/ */
@property({ type: Boolean }) enableMillisecond = false; @property({ type: Boolean }) enableMillisecond = false;
/**
* show the day field
*/
@property({ type: Boolean }) enableDay = false;
/** /**
* limit hours input * limit hours input
*/ */
@@ -110,6 +131,29 @@ export class HaBaseTimeInput extends LitElement {
return html` return html`
${this.label ? html`<label>${this.label}</label>` : ""} ${this.label ? html`<label>${this.label}</label>` : ""}
<div class="time-input-wrap"> <div class="time-input-wrap">
${this.enableDay
? html`
<ha-textfield
id="day"
type="number"
inputmode="numeric"
.value=${this.days}
.label=${this.dayLabel}
name="days"
@input=${this._valueChanged}
@focus=${this._onFocus}
no-spinner
.required=${this.required}
.autoValidate=${this.autoValidate}
min="0"
.disabled=${this.disabled}
suffix=":"
class="hasSuffix"
>
</ha-textfield>
`
: ""}
<ha-textfield <ha-textfield
id="hour" id="hour"
type="number" type="number"
@@ -207,6 +251,7 @@ export class HaBaseTimeInput extends LitElement {
<mwc-list-item value="PM">PM</mwc-list-item> <mwc-list-item value="PM">PM</mwc-list-item>
</ha-select>`} </ha-select>`}
</div> </div>
${this.helper ? html`<div class="helper">${this.helper}</div>` : ""}
`; `;
} }
@@ -303,6 +348,13 @@ export class HaBaseTimeInput extends LitElement {
color: var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87)); color: var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87));
padding-left: 4px; padding-left: 4px;
} }
.helper {
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
font-size: 0.75rem;
padding-left: 16px;
padding-right: 16px;
}
`; `;
} }

View File

@@ -5,6 +5,7 @@ import "./ha-base-time-input";
import type { TimeChangedEvent } from "./ha-base-time-input"; import type { TimeChangedEvent } from "./ha-base-time-input";
export interface HaDurationData { export interface HaDurationData {
days?: number;
hours?: number; hours?: number;
minutes?: number; minutes?: number;
seconds?: number; seconds?: number;
@@ -17,10 +18,14 @@ class HaDurationInput extends LitElement {
@property() public label?: string; @property() public label?: string;
@property() public helper?: string;
@property({ type: Boolean }) public required?: boolean; @property({ type: Boolean }) public required?: boolean;
@property({ type: Boolean }) public enableMillisecond?: boolean; @property({ type: Boolean }) public enableMillisecond?: boolean;
@property({ type: Boolean }) public enableDay?: boolean;
@property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public disabled = false;
@query("paper-time-input", true) private _input?: HTMLElement; @query("paper-time-input", true) private _input?: HTMLElement;
@@ -35,19 +40,23 @@ class HaDurationInput extends LitElement {
return html` return html`
<ha-base-time-input <ha-base-time-input
.label=${this.label} .label=${this.label}
.helper=${this.helper}
.required=${this.required} .required=${this.required}
.autoValidate=${this.required} .autoValidate=${this.required}
.disabled=${this.disabled} .disabled=${this.disabled}
errorMessage="Required" errorMessage="Required"
enableSecond enableSecond
.enableMillisecond=${this.enableMillisecond} .enableMillisecond=${this.enableMillisecond}
.enableDay=${this.enableDay}
format="24" format="24"
.days=${this._days}
.hours=${this._hours} .hours=${this._hours}
.minutes=${this._minutes} .minutes=${this._minutes}
.seconds=${this._seconds} .seconds=${this._seconds}
.milliseconds=${this._milliseconds} .milliseconds=${this._milliseconds}
@value-changed=${this._durationChanged} @value-changed=${this._durationChanged}
noHoursLimit noHoursLimit
dayLabel="dd"
hourLabel="hh" hourLabel="hh"
minLabel="mm" minLabel="mm"
secLabel="ss" secLabel="ss"
@@ -56,6 +65,10 @@ class HaDurationInput extends LitElement {
`; `;
} }
private get _days() {
return this.data?.days ? Number(this.data.days) : 0;
}
private get _hours() { private get _hours() {
return this.data?.hours ? Number(this.data.hours) : 0; return this.data?.hours ? Number(this.data.hours) : 0;
} }
@@ -94,6 +107,11 @@ class HaDurationInput extends LitElement {
value.minutes %= 60; value.minutes %= 60;
} }
if (this.enableDay && value.hours > 24) {
value.days = (value.days ?? 0) + Math.floor(value.hours / 24);
value.hours %= 24;
}
fireEvent(this, "value-changed", { fireEvent(this, "value-changed", {
value, value,
}); });

View File

@@ -34,12 +34,25 @@ export const computeInitialHaFormData = (
}; };
} else if ("selector" in field) { } else if ("selector" in field) {
const selector: Selector = field.selector; const selector: Selector = field.selector;
if ("boolean" in selector) {
if ("device" in selector) {
data[field.name] = selector.device.multiple ? [] : "";
} else if ("entity" in selector) {
data[field.name] = selector.entity.multiple ? [] : "";
} else if ("area" in selector) {
data[field.name] = selector.area.multiple ? [] : "";
} else if ("boolean" in selector) {
data[field.name] = false; data[field.name] = false;
} else if ("text" in selector) { } else if (
"text" in selector ||
"addon" in selector ||
"attribute" in selector ||
"icon" in selector ||
"theme" in selector
) {
data[field.name] = ""; data[field.name] = "";
} else if ("number" in selector) { } else if ("number" in selector) {
data[field.name] = "min" in selector.number ? selector.number.min : 0; data[field.name] = selector.number.min ?? 0;
} else if ("select" in selector) { } else if ("select" in selector) {
if (selector.select.options.length) { if (selector.select.options.length) {
data[field.name] = selector.select.options[0][0]; data[field.name] = selector.select.options[0][0];
@@ -50,6 +63,23 @@ export const computeInitialHaFormData = (
minutes: 0, minutes: 0,
seconds: 0, seconds: 0,
}; };
} else if ("time" in selector) {
data[field.name] = "00:00:00";
} else if ("date" in selector || "datetime" in selector) {
const now = new Date().toISOString().slice(0, 10);
data[field.name] = `${now} 00:00:00`;
} else if ("color_rgb" in selector) {
data[field.name] = [0, 0, 0];
} else if ("color_temp" in selector) {
data[field.name] = selector.color_temp.min_mireds ?? 153;
} else if (
"action" in selector ||
"media" in selector ||
"target" in selector
) {
data[field.name] = {};
} else {
throw new Error("Selector not supported in initial form data");
} }
} }
}); });

View File

@@ -28,6 +28,8 @@ export class HaFormString extends LitElement implements HaFormElement {
@property() public label!: string; @property() public label!: string;
@property() public helper?: string;
@property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public disabled = false;
@state() private _unmaskedPassword = false; @state() private _unmaskedPassword = false;
@@ -53,6 +55,8 @@ export class HaFormString extends LitElement implements HaFormElement {
: "password"} : "password"}
.label=${this.label} .label=${this.label}
.value=${this.data || ""} .value=${this.data || ""}
.helper=${this.helper}
helperPersistent
.disabled=${this.disabled} .disabled=${this.disabled}
.required=${this.schema.required} .required=${this.schema.required}
.autoValidate=${this.schema.required} .autoValidate=${this.schema.required}

View File

@@ -6,6 +6,7 @@ import { EntityRegistryEntry } from "../../data/entity_registry";
import { AreaSelector } from "../../data/selector"; import { AreaSelector } from "../../data/selector";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import "../ha-area-picker"; import "../ha-area-picker";
import "../ha-areas-picker";
@customElement("ha-selector-area") @customElement("ha-selector-area")
export class HaAreaSelector extends LitElement { export class HaAreaSelector extends LitElement {
@@ -38,21 +39,43 @@ export class HaAreaSelector extends LitElement {
} }
protected render() { protected render() {
return html`<ha-area-picker if (!this.selector.area.multiple) {
.hass=${this.hass} return html`
.value=${this.value} <ha-area-picker
.label=${this.label} .hass=${this.hass}
no-add .value=${this.value}
.deviceFilter=${this._filterDevices} .label=${this.label}
.entityFilter=${this._filterEntities} no-add
.includeDeviceClasses=${this.selector.area.entity?.device_class .deviceFilter=${this._filterDevices}
? [this.selector.area.entity.device_class] .entityFilter=${this._filterEntities}
: undefined} .includeDeviceClasses=${this.selector.area.entity?.device_class
.includeDomains=${this.selector.area.entity?.domain ? [this.selector.area.entity.device_class]
? [this.selector.area.entity.domain] : undefined}
: undefined} .includeDomains=${this.selector.area.entity?.domain
.disabled=${this.disabled} ? [this.selector.area.entity.domain]
></ha-area-picker>`; : undefined}
.disabled=${this.disabled}
></ha-area-picker>
`;
}
return html`
<ha-areas-picker
.hass=${this.hass}
.value=${this.value}
.pickAreaLabel=${this.label}
no-add
.deviceFilter=${this._filterDevices}
.entityFilter=${this._filterEntities}
.includeDeviceClasses=${this.selector.area.entity?.device_class
? [this.selector.area.entity.device_class]
: undefined}
.includeDomains=${this.selector.area.entity?.domain
? [this.selector.area.entity.domain]
: undefined}
.disabled=${this.disabled}
></ha-areas-picker>
`;
} }
private _filterEntities = (entity: EntityRegistryEntry): boolean => { private _filterEntities = (entity: EntityRegistryEntry): boolean => {

View File

@@ -14,6 +14,8 @@ export class HaTimeDuration extends LitElement {
@property() public label?: string; @property() public label?: string;
@property() public helper?: string;
@property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public required = true; @property({ type: Boolean }) public required = true;
@@ -22,9 +24,11 @@ export class HaTimeDuration extends LitElement {
return html` return html`
<ha-duration-input <ha-duration-input
.label=${this.label} .label=${this.label}
.helper=${this.helper}
.data=${this.value} .data=${this.value}
.disabled=${this.disabled} .disabled=${this.disabled}
.required=${this.required} .required=${this.required}
.enableDay=${this.selector.duration.enable_day}
></ha-duration-input> ></ha-duration-input>
`; `;
} }

View File

@@ -33,7 +33,6 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
.excludeEntities=${this.selector.entity.exclude_entities} .excludeEntities=${this.selector.entity.exclude_entities}
.entityFilter=${this._filterEntities} .entityFilter=${this._filterEntities}
.disabled=${this.disabled} .disabled=${this.disabled}
.itemValuePath=${!this.selector.entity.use_uuid ? "entity_id" : "id"}
allow-custom-entity allow-custom-entity
></ha-entity-picker>`; ></ha-entity-picker>`;
} }

View File

@@ -6,7 +6,6 @@ import type {
LocationSelector, LocationSelector,
LocationSelectorValue, LocationSelectorValue,
} from "../../data/selector"; } from "../../data/selector";
import "../../panels/lovelace/components/hui-theme-select-editor";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import type { MarkerLocation } from "../map/ha-locations-editor"; import type { MarkerLocation } from "../map/ha-locations-editor";
import "../map/ha-locations-editor"; import "../map/ha-locations-editor";
@@ -52,7 +51,10 @@ export class HaLocationSelector extends LitElement {
longitude: value?.longitude || this.hass.config.longitude, longitude: value?.longitude || this.hass.config.longitude,
radius: selector.location.radius ? value?.radius || 1000 : undefined, radius: selector.location.radius ? value?.radius || 1000 : undefined,
radius_color: zoneRadiusColor, radius_color: zoneRadiusColor,
icon: selector.location.icon, icon:
selector.location.icon || selector.location.radius
? "mdi:map-marker-radius"
: "mdi:map-marker",
location_editable: true, location_editable: true,
radius_editable: true, radius_editable: true,
}, },

View File

@@ -19,6 +19,8 @@ export class HaNumberSelector extends LitElement {
@property() public label?: string; @property() public label?: string;
@property() public helper?: string;
@property({ type: Boolean }) public required = true; @property({ type: Boolean }) public required = true;
@property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public disabled = false;
@@ -48,6 +50,8 @@ export class HaNumberSelector extends LitElement {
.max=${this.selector.number.max} .max=${this.selector.number.max}
.value=${this.value ?? ""} .value=${this.value ?? ""}
.step=${this.selector.number.step ?? 1} .step=${this.selector.number.step ?? 1}
helperPersistent
.helper=${this.helper}
.disabled=${this.disabled} .disabled=${this.disabled}
.required=${this.required} .required=${this.required}
.suffix=${this.selector.number.unit_of_measurement} .suffix=${this.selector.number.unit_of_measurement}

View File

@@ -18,6 +18,8 @@ export class HaTextSelector extends LitElement {
@property() public placeholder?: string; @property() public placeholder?: string;
@property() public helper?: string;
@property() public selector!: StringSelector; @property() public selector!: StringSelector;
@property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public disabled = false;
@@ -32,6 +34,8 @@ export class HaTextSelector extends LitElement {
.label=${this.label} .label=${this.label}
.placeholder=${this.placeholder} .placeholder=${this.placeholder}
.value=${this.value || ""} .value=${this.value || ""}
.helper=${this.helper}
helperPersistent
.disabled=${this.disabled} .disabled=${this.disabled}
@input=${this._handleChange} @input=${this._handleChange}
autocapitalize="none" autocapitalize="none"
@@ -44,6 +48,8 @@ export class HaTextSelector extends LitElement {
return html`<ha-textfield return html`<ha-textfield
.value=${this.value || ""} .value=${this.value || ""}
.placeholder=${this.placeholder || ""} .placeholder=${this.placeholder || ""}
.helper=${this.helper}
helperPersistent
.disabled=${this.disabled} .disabled=${this.disabled}
.type=${this._unmaskedPassword ? "text" : this.selector.text?.type} .type=${this._unmaskedPassword ? "text" : this.selector.text?.type}
@input=${this._handleChange} @input=${this._handleChange}

View File

@@ -1,4 +1,4 @@
import "../../panels/lovelace/components/hui-theme-select-editor"; import "../ha-theme-picker";
import { html, LitElement } from "lit"; import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
@@ -18,11 +18,11 @@ export class HaThemeSelector extends LitElement {
protected render() { protected render() {
return html` return html`
<hui-theme-select-editor <ha-theme-picker
.hass=${this.hass} .hass=${this.hass}
.value=${this.value} .value=${this.value}
.label=${this.label} .label=${this.label}
></hui-theme-select-editor> ></ha-theme-picker>
`; `;
} }
} }

View File

@@ -471,6 +471,7 @@ export class HaServiceControl extends LitElement {
} }
ha-settings-row { ha-settings-row {
--paper-time-input-justify-content: flex-end; --paper-time-input-justify-content: flex-end;
--settings-row-content-width: 100%;
border-top: var( border-top: var(
--service-control-items-border-top, --service-control-items-border-top,
1px solid var(--divider-color) 1px solid var(--divider-color)
@@ -489,9 +490,6 @@ export class HaServiceControl extends LitElement {
margin: var(--service-control-padding, 0 16px); margin: var(--service-control-padding, 0 16px);
padding: 16px 0; padding: 16px 0;
} }
:host(:not([narrow])) ha-settings-row ha-selector {
width: 60%;
}
.checkbox-spacer { .checkbox-spacer {
width: 32px; width: 32px;
} }

View File

@@ -21,7 +21,7 @@ export class HaSettingsRow extends LitElement {
<div secondary><slot name="description"></slot></div> <div secondary><slot name="description"></slot></div>
</paper-item-body> </paper-item-body>
</div> </div>
<slot></slot> <div class="content"><slot></slot></div>
`; `;
} }
@@ -43,6 +43,18 @@ export class HaSettingsRow extends LitElement {
); );
flex: 1; flex: 1;
} }
.content {
display: contents;
}
:host(:not([narrow])) .content {
display: flex;
justify-content: flex-end;
flex: 1;
padding: 16px 0;
}
.content ::slotted(*) {
width: var(--settings-row-content-width);
}
:host([narrow]) { :host([narrow]) {
align-items: normal; align-items: normal;
flex-direction: column; flex-direction: column;

View File

@@ -37,6 +37,7 @@ import { LocalStorage } from "../common/decorators/local-storage";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { toggleAttribute } from "../common/dom/toggle_attribute"; import { toggleAttribute } from "../common/dom/toggle_attribute";
import { computeDomain } from "../common/entity/compute_domain"; import { computeDomain } from "../common/entity/compute_domain";
import { computeStateDomain } from "../common/entity/compute_state_domain";
import { stringCompare } from "../common/string/compare"; import { stringCompare } from "../common/string/compare";
import { computeRTL } from "../common/util/compute_rtl"; import { computeRTL } from "../common/util/compute_rtl";
import { ActionHandlerDetail } from "../data/lovelace"; import { ActionHandlerDetail } from "../data/lovelace";
@@ -44,6 +45,7 @@ import {
PersistentNotification, PersistentNotification,
subscribeNotifications, subscribeNotifications,
} from "../data/persistent_notification"; } from "../data/persistent_notification";
import { updateCanInstall, UpdateEntity } from "../data/update";
import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive"; import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive";
import { haStyleScrollbar } from "../resources/styles"; import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant, PanelInfo, Route } from "../types"; import type { HomeAssistant, PanelInfo, Route } from "../types";
@@ -68,7 +70,6 @@ const SORT_VALUE_URL_PATHS = {
const PANEL_ICONS = { const PANEL_ICONS = {
calendar: mdiCalendar, calendar: mdiCalendar,
config: mdiCog,
"developer-tools": mdiHammer, "developer-tools": mdiHammer,
energy: mdiLightningBolt, energy: mdiLightningBolt,
history: mdiChartBox, history: mdiChartBox,
@@ -190,6 +191,8 @@ class HaSidebar extends LitElement {
@state() private _notifications?: PersistentNotification[]; @state() private _notifications?: PersistentNotification[];
@state() private _updatesCount = 0;
@state() private _renderEmptySortable = false; @state() private _renderEmptySortable = false;
private _mouseLeaveTimeout?: number; private _mouseLeaveTimeout?: number;
@@ -235,6 +238,7 @@ class HaSidebar extends LitElement {
changedProps.has("narrow") || changedProps.has("narrow") ||
changedProps.has("alwaysExpand") || changedProps.has("alwaysExpand") ||
changedProps.has("_externalConfig") || changedProps.has("_externalConfig") ||
changedProps.has("_updatesCount") ||
changedProps.has("_notifications") || changedProps.has("_notifications") ||
changedProps.has("editMode") || changedProps.has("editMode") ||
changedProps.has("_renderEmptySortable") || changedProps.has("_renderEmptySortable") ||
@@ -290,6 +294,12 @@ class HaSidebar extends LitElement {
toggleAttribute(this, "rtl", computeRTL(this.hass)); toggleAttribute(this, "rtl", computeRTL(this.hass));
} }
this._updatesCount = Object.values(this.hass.states).filter(
(entity) =>
computeStateDomain(entity) === "update" &&
updateCanInstall(entity as UpdateEntity)
).length;
if (!SUPPORT_SCROLL_IF_NEEDED) { if (!SUPPORT_SCROLL_IF_NEEDED) {
return; return;
} }
@@ -387,35 +397,37 @@ class HaSidebar extends LitElement {
icon?: string | null, icon?: string | null,
iconPath?: string | null iconPath?: string | null
) { ) {
return html` return urlPath === "config"
<a ? this._renderConfiguration(title)
role="option" : html`
href=${`/${urlPath}`} <a
data-panel=${urlPath} role="option"
tabindex="-1" href=${`/${urlPath}`}
@mouseenter=${this._itemMouseEnter} data-panel=${urlPath}
@mouseleave=${this._itemMouseLeave} tabindex="-1"
> @mouseenter=${this._itemMouseEnter}
<paper-icon-item> @mouseleave=${this._itemMouseLeave}
${iconPath >
? html`<ha-svg-icon <paper-icon-item>
slot="item-icon" ${iconPath
.path=${iconPath} ? html`<ha-svg-icon
></ha-svg-icon>` slot="item-icon"
: html`<ha-icon slot="item-icon" .icon=${icon}></ha-icon>`} .path=${iconPath}
<span class="item-text">${title}</span> ></ha-svg-icon>`
</paper-icon-item> : html`<ha-icon slot="item-icon" .icon=${icon}></ha-icon>`}
${this.editMode <span class="item-text">${title}</span>
? html`<ha-icon-button </paper-icon-item>
.label=${this.hass.localize("ui.sidebar.hide_panel")} ${this.editMode
.path=${mdiClose} ? html`<ha-icon-button
class="hide-panel" .label=${this.hass.localize("ui.sidebar.hide_panel")}
.panel=${urlPath} .path=${mdiClose}
@click=${this._hidePanel} class="hide-panel"
></ha-icon-button>` .panel=${urlPath}
: ""} @click=${this._hidePanel}
</a> ></ha-icon-button>`
`; : ""}
</a>
`;
} }
private _renderPanelsEdit(beforeSpacer: PanelInfo[]) { private _renderPanelsEdit(beforeSpacer: PanelInfo[]) {
@@ -477,6 +489,35 @@ class HaSidebar extends LitElement {
return html`<div class="spacer" disabled></div>`; return html`<div class="spacer" disabled></div>`;
} }
private _renderConfiguration(title: string | null) {
return html` <a
class="configuration-container"
role="option"
href="/config"
data-panel="config"
tabindex="-1"
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
>
<paper-icon-item class="configuration" role="option">
<ha-svg-icon slot="item-icon" .path=${mdiCog}></ha-svg-icon>
${!this.alwaysExpand && this._updatesCount > 0
? html`
<span class="configuration-badge" slot="item-icon">
${this._updatesCount}
</span>
`
: ""}
<span class="item-text">${title}</span>
${this.alwaysExpand && this._updatesCount > 0
? html`
<span class="configuration-badge">${this._updatesCount}</span>
`
: ""}
</paper-icon-item>
</a>`;
}
private _renderNotifications() { private _renderNotifications() {
let notificationCount = this._notifications let notificationCount = this._notifications
? this._notifications.length ? this._notifications.length
@@ -953,18 +994,21 @@ class HaSidebar extends LitElement {
height: 1px; height: 1px;
background-color: var(--divider-color); background-color: var(--divider-color);
} }
.notifications-container { .notifications-container,
.configuration-container {
display: flex; display: flex;
margin-left: env(safe-area-inset-left); margin-left: env(safe-area-inset-left);
} }
:host([rtl]) .notifications-container { :host([rtl]) .notifications-container,
:host([rtl]) .configuration-container {
margin-left: initial; margin-left: initial;
margin-right: env(safe-area-inset-right); margin-right: env(safe-area-inset-right);
} }
.notifications { .notifications {
cursor: pointer; cursor: pointer;
} }
.notifications .item-text { .notifications .item-text,
.configuration .item-text {
flex: 1; flex: 1;
} }
.profile { .profile {
@@ -988,7 +1032,8 @@ class HaSidebar extends LitElement {
margin-right: 8px; margin-right: 8px;
} }
.notification-badge { .notification-badge,
.configuration-badge {
min-width: 20px; min-width: 20px;
box-sizing: border-box; box-sizing: border-box;
border-radius: 50%; border-radius: 50%;
@@ -999,7 +1044,11 @@ class HaSidebar extends LitElement {
padding: 0px 6px; padding: 0px 6px;
color: var(--text-accent-color, var(--text-primary-color)); color: var(--text-accent-color, var(--text-primary-color));
} }
ha-svg-icon + .notification-badge { .configuration-badge {
background-color: var(--primary-color);
}
ha-svg-icon + .notification-badge,
ha-svg-icon + .configuration-badge {
position: absolute; position: absolute;
bottom: 14px; bottom: 14px;
left: 26px; left: 26px;

View File

@@ -2,13 +2,13 @@ import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../../../common/dom/stop_propagation"; import { stopPropagation } from "../common/dom/stop_propagation";
import "../../../components/ha-select"; import "./ha-select";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../types";
@customElement("hui-theme-select-editor") @customElement("ha-theme-picker")
export class HuiThemeSelectEditor extends LitElement { export class HaThemePicker extends LitElement {
@property() public value?: string; @property() public value?: string;
@property() public label?: string; @property() public label?: string;
@@ -19,11 +19,7 @@ export class HuiThemeSelectEditor extends LitElement {
return html` return html`
<ha-select <ha-select
.label=${this.label || .label=${this.label ||
`${this.hass!.localize( this.hass!.localize("ui.components.theme_picker.theme")}
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`}
.value=${this.value} .value=${this.value}
@selected=${this._changed} @selected=${this._changed}
@closed=${stopPropagation} @closed=${stopPropagation}
@@ -32,7 +28,7 @@ export class HuiThemeSelectEditor extends LitElement {
> >
<mwc-list-item value="remove" <mwc-list-item value="remove"
>${this.hass!.localize( >${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.no_theme" "ui.components.theme_picker.no_theme"
)}</mwc-list-item )}</mwc-list-item
> >
${Object.keys(this.hass!.themes.themes) ${Object.keys(this.hass!.themes.themes)
@@ -64,6 +60,6 @@ export class HuiThemeSelectEditor extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"hui-theme-select-editor": HuiThemeSelectEditor; "ha-theme-picker": HaThemePicker;
} }
} }

View File

@@ -488,6 +488,14 @@ export class HaMap extends ReactiveElement {
text-align: center; text-align: center;
color: var(--primary-text-color); color: var(--primary-text-color);
} }
.leaflet-pane {
z-index: 0 !important;
}
.leaflet-control,
.leaflet-top,
.leaflet-bottom {
z-index: 1 !important;
}
`; `;
} }
} }

View File

@@ -62,6 +62,7 @@ export interface ContextConstraint {
export interface BaseTrigger { export interface BaseTrigger {
platform: string; platform: string;
id?: string; id?: string;
variables?: Record<string, unknown>;
} }
export interface StateTrigger extends BaseTrigger { export interface StateTrigger extends BaseTrigger {

View File

@@ -46,6 +46,7 @@ export interface AreaSelector {
manufacturer?: DeviceSelector["device"]["manufacturer"]; manufacturer?: DeviceSelector["device"]["manufacturer"];
model?: DeviceSelector["device"]["model"]; model?: DeviceSelector["device"]["model"];
}; };
multiple?: boolean;
}; };
} }
@@ -96,8 +97,9 @@ export interface DeviceSelector {
} }
export interface DurationSelector { export interface DurationSelector {
// eslint-disable-next-line @typescript-eslint/ban-types duration: {
duration: {}; enable_day?: boolean;
};
} }
export interface EntitySelector { export interface EntitySelector {
@@ -106,7 +108,6 @@ export interface EntitySelector {
domain?: string | string[]; domain?: string | string[];
device_class?: string; device_class?: string;
multiple?: boolean; multiple?: boolean;
use_uuid?: boolean;
include_entities?: string[]; include_entities?: string[];
exclude_entities?: string[]; exclude_entities?: string[];
}; };

View File

@@ -14,9 +14,7 @@ import { fireEvent, HASSDomEvent } from "../../common/dom/fire_event";
import { computeRTL } from "../../common/util/compute_rtl"; import { computeRTL } from "../../common/util/compute_rtl";
import "../../components/ha-circular-progress"; import "../../components/ha-circular-progress";
import "../../components/ha-dialog"; import "../../components/ha-dialog";
import "../../components/ha-form/ha-form";
import "../../components/ha-icon-button"; import "../../components/ha-icon-button";
import "../../components/ha-markdown";
import { import {
AreaRegistryEntry, AreaRegistryEntry,
subscribeAreaRegistry, subscribeAreaRegistry,

View File

@@ -91,6 +91,12 @@ export const showConfigFlowDialog = (
); );
}, },
renderShowFormStepFieldHelper(hass, step, field) {
return hass.localize(
`component.${step.handler}.config.step.${step.step_id}.data_description.${field.name}`
);
},
renderShowFormStepFieldError(hass, step, error) { renderShowFormStepFieldError(hass, step, error) {
return hass.localize( return hass.localize(
`component.${step.handler}.config.error.${error}`, `component.${step.handler}.config.error.${error}`,

View File

@@ -50,6 +50,12 @@ export interface FlowConfig {
field: HaFormSchema field: HaFormSchema
): string; ): string;
renderShowFormStepFieldHelper(
hass: HomeAssistant,
step: DataEntryFlowStepForm,
field: HaFormSchema
): string;
renderShowFormStepFieldError( renderShowFormStepFieldError(
hass: HomeAssistant, hass: HomeAssistant,
step: DataEntryFlowStepForm, step: DataEntryFlowStepForm,

View File

@@ -89,6 +89,12 @@ export const showOptionsFlowDialog = (
); );
}, },
renderShowFormStepFieldHelper(hass, step, field) {
return hass.localize(
`component.${configEntry.domain}.options.step.${step.step_id}.data_description.${field.name}`
);
},
renderShowFormStepFieldError(hass, step, error) { renderShowFormStepFieldError(hass, step, error) {
return hass.localize( return hass.localize(
`component.${configEntry.domain}.options.error.${error}`, `component.${configEntry.domain}.options.error.${error}`,

View File

@@ -54,6 +54,7 @@ class StepFlowForm extends LitElement {
.schema=${step.data_schema} .schema=${step.data_schema}
.error=${step.errors} .error=${step.errors}
.computeLabel=${this._labelCallback} .computeLabel=${this._labelCallback}
.computeHelper=${this._helperCallback}
.computeError=${this._errorCallback} .computeError=${this._errorCallback}
></ha-form> ></ha-form>
</div> </div>
@@ -166,6 +167,9 @@ class StepFlowForm extends LitElement {
private _labelCallback = (field: HaFormSchema): string => private _labelCallback = (field: HaFormSchema): string =>
this.flowConfig.renderShowFormStepFieldLabel(this.hass, this.step, field); this.flowConfig.renderShowFormStepFieldLabel(this.hass, this.step, field);
private _helperCallback = (field: HaFormSchema): string =>
this.flowConfig.renderShowFormStepFieldHelper(this.hass, this.step, field);
private _errorCallback = (error: string) => private _errorCallback = (error: string) =>
this.flowConfig.renderShowFormStepFieldError(this.hass, this.step, error); this.flowConfig.renderShowFormStepFieldError(this.hass, this.step, error);

View File

@@ -326,12 +326,9 @@ export class HaBlueprintAutomationEditor extends LitElement {
} }
ha-settings-row { ha-settings-row {
--paper-time-input-justify-content: flex-end; --paper-time-input-justify-content: flex-end;
--settings-row-content-width: 100%;
border-top: 1px solid var(--divider-color); border-top: 1px solid var(--divider-color);
} }
:host(:not([narrow])) ha-settings-row ha-textfield,
:host(:not([narrow])) ha-settings-row ha-selector {
width: 60%;
}
`, `,
]; ];
} }

View File

@@ -1,6 +1,5 @@
import "@material/mwc-formfield/mwc-formfield";
import "../../../components/ha-radio";
import "@material/mwc-button/mwc-button"; import "@material/mwc-button/mwc-button";
import "@material/mwc-formfield/mwc-formfield";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket"; import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { import {
@@ -20,9 +19,15 @@ import "../../../components/ha-alert";
import "../../../components/ha-area-picker"; import "../../../components/ha-area-picker";
import "../../../components/ha-expansion-panel"; import "../../../components/ha-expansion-panel";
import "../../../components/ha-icon-picker"; import "../../../components/ha-icon-picker";
import "../../../components/ha-radio";
import "../../../components/ha-select"; import "../../../components/ha-select";
import "../../../components/ha-switch"; import "../../../components/ha-switch";
import "../../../components/ha-textfield"; import "../../../components/ha-textfield";
import {
ConfigEntry,
deleteConfigEntry,
getConfigEntries,
} from "../../../data/config_entries";
import { import {
DeviceRegistryEntry, DeviceRegistryEntry,
subscribeDeviceRegistry, subscribeDeviceRegistry,
@@ -34,6 +39,7 @@ import {
removeEntityRegistryEntry, removeEntityRegistryEntry,
updateEntityRegistryEntry, updateEntityRegistryEntry,
} from "../../../data/entity_registry"; } from "../../../data/entity_registry";
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
import { import {
showAlertDialog, showAlertDialog,
showConfirmationDialog, showConfirmationDialog,
@@ -42,27 +48,39 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail"; import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail";
import {
ConfigEntry,
deleteConfigEntry,
getConfigEntries,
} from "../../../data/config_entries";
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
const OVERRIDE_DEVICE_CLASSES = { const OVERRIDE_DEVICE_CLASSES = {
cover: [ cover: [
"awning", [
"blind", "awning",
"curtain", "blind",
"damper", "curtain",
"door", "damper",
"garage", "door",
"gate", "garage",
"shade", "gate",
"shutter", "shade",
"window", "shutter",
"window",
],
],
binary_sensor: [
["lock"], // Lock
["window", "door", "garage_door", "opening"], // Door
["battery", "battery_charging"], // Battery
["cold", "gas", "heat"], // Climate
["running", "motion", "moving", "occupancy", "presence", "vibration"], // Presence
["power", "plug", "light"], // Power
[
"smoke",
"safety",
"sound",
"problem",
"tamper",
"carbon_monoxide",
"moisture",
], // Alarm
], ],
binary_sensor: ["window", "door", "garage_door", "opening"],
}; };
@customElement("entity-registry-settings") @customElement("entity-registry-settings")
@@ -85,8 +103,6 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
@state() private _hiddenBy!: string | null; @state() private _hiddenBy!: string | null;
private _deviceLookup?: Record<string, DeviceRegistryEntry>;
@state() private _device?: DeviceRegistryEntry; @state() private _device?: DeviceRegistryEntry;
@state() private _helperConfigEntry?: ConfigEntry; @state() private _helperConfigEntry?: ConfigEntry;
@@ -97,6 +113,10 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
private _origEntityId!: string; private _origEntityId!: string;
private _deviceLookup?: Record<string, DeviceRegistryEntry>;
private _deviceClassOptions?: string[][];
public hassSubscribe(): UnsubscribeFunc[] { public hassSubscribe(): UnsubscribeFunc[] {
return [ return [
subscribeDeviceRegistry(this.hass.connection!, (devices) => { subscribeDeviceRegistry(this.hass.connection!, (devices) => {
@@ -125,23 +145,41 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
} }
} }
protected updated(changedProperties: PropertyValues) { protected willUpdate(changedProperties: PropertyValues) {
super.updated(changedProperties); super.willUpdate(changedProperties);
if (changedProperties.has("entry")) { if (!changedProperties.has("entry")) {
this._error = undefined; return;
this._name = this.entry.name || ""; }
this._icon = this.entry.icon || "";
this._deviceClass = this._error = undefined;
this.entry.device_class || this.entry.original_device_class; this._name = this.entry.name || "";
this._origEntityId = this.entry.entity_id; this._icon = this.entry.icon || "";
this._areaId = this.entry.area_id; this._deviceClass =
this._entityId = this.entry.entity_id; this.entry.device_class || this.entry.original_device_class;
this._disabledBy = this.entry.disabled_by; this._origEntityId = this.entry.entity_id;
this._hiddenBy = this.entry.hidden_by; this._areaId = this.entry.area_id;
this._device = this._entityId = this.entry.entity_id;
this.entry.device_id && this._deviceLookup this._disabledBy = this.entry.disabled_by;
? this._deviceLookup[this.entry.device_id] this._hiddenBy = this.entry.hidden_by;
: undefined; this._device =
this.entry.device_id && this._deviceLookup
? this._deviceLookup[this.entry.device_id]
: undefined;
const domain = computeDomain(this.entry.entity_id);
const deviceClasses: string[][] = OVERRIDE_DEVICE_CLASSES[domain];
if (!deviceClasses) {
return;
}
this._deviceClassOptions = [[], []];
for (const deviceClass of deviceClasses) {
if (deviceClass.includes(this.entry.original_device_class!)) {
this._deviceClassOptions[0] = deviceClass;
} else {
this._deviceClassOptions[1].push(...deviceClass);
}
} }
} }
@@ -197,28 +235,39 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
: undefined} : undefined}
.disabled=${this._submitting} .disabled=${this._submitting}
></ha-icon-picker> ></ha-icon-picker>
${OVERRIDE_DEVICE_CLASSES[domain]?.includes(this._deviceClass) || ${this._deviceClassOptions
(domain === "cover" && this.entry.original_device_class === null) ? html`
? html`<ha-select <ha-select
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.dialogs.entity_registry.editor.device_class" "ui.dialogs.entity_registry.editor.device_class"
)} )}
.value=${this._deviceClass} .value=${this._deviceClass}
naturalMenuWidth naturalMenuWidth
fixedMenuPosition fixedMenuPosition
@selected=${this._deviceClassChanged} @selected=${this._deviceClassChanged}
@closed=${stopPropagation} @closed=${stopPropagation}
> >
${OVERRIDE_DEVICE_CLASSES[domain].map( ${this._deviceClassOptions[0].map(
(deviceClass: string) => html` (deviceClass: string) => html`
<mwc-list-item .value=${deviceClass}> <mwc-list-item .value=${deviceClass} test=${deviceClass}>
${this.hass.localize( ${this.hass.localize(
`ui.dialogs.entity_registry.editor.device_classes.${domain}.${deviceClass}` `ui.dialogs.entity_registry.editor.device_classes.${domain}.${deviceClass}`
)} )}
</mwc-list-item> </mwc-list-item>
` `
)} )}
</ha-select>` <li divider role="separator"></li>
${this._deviceClassOptions[1].map(
(deviceClass: string) => html`
<mwc-list-item .value=${deviceClass} test=${deviceClass}>
${this.hass.localize(
`ui.dialogs.entity_registry.editor.device_classes.${domain}.${deviceClass}`
)}
</mwc-list-item>
`
)}
</ha-select>
`
: ""} : ""}
<ha-textfield <ha-textfield
error-message="Domain needs to stay the same" error-message="Domain needs to stay the same"
@@ -264,7 +313,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
)}: )}:
</div> </div>
<div class="secondary"> <div class="secondary">
${this._disabledBy && this._disabledBy !== "user" ${this._disabledBy &&
this._disabledBy !== "user" &&
this._disabledBy !== "integration"
? this.hass.localize( ? this.hass.localize(
"ui.dialogs.entity_registry.editor.enabled_cause", "ui.dialogs.entity_registry.editor.enabled_cause",
"cause", "cause",
@@ -286,7 +337,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
.checked=${!this._hiddenBy && !this._disabledBy} .checked=${!this._hiddenBy && !this._disabledBy}
.disabled=${(this._hiddenBy && this._hiddenBy !== "user") || .disabled=${(this._hiddenBy && this._hiddenBy !== "user") ||
this._device?.disabled_by || this._device?.disabled_by ||
(this._disabledBy && this._disabledBy !== "user")} (this._disabledBy &&
this._disabledBy !== "user" &&
this._disabledBy !== "integration")}
@change=${this._viewStatusChanged} @change=${this._viewStatusChanged}
></ha-radio> ></ha-radio>
</mwc-formfield> </mwc-formfield>
@@ -301,7 +354,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
.checked=${this._hiddenBy !== null} .checked=${this._hiddenBy !== null}
.disabled=${(this._hiddenBy && this._hiddenBy !== "user") || .disabled=${(this._hiddenBy && this._hiddenBy !== "user") ||
Boolean(this._device?.disabled_by) || Boolean(this._device?.disabled_by) ||
(this._disabledBy && this._disabledBy !== "user")} (this._disabledBy &&
this._disabledBy !== "user" &&
this._disabledBy !== "integration")}
@change=${this._viewStatusChanged} @change=${this._viewStatusChanged}
></ha-radio> ></ha-radio>
</mwc-formfield> </mwc-formfield>
@@ -316,7 +371,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
.checked=${this._disabledBy !== null} .checked=${this._disabledBy !== null}
.disabled=${(this._hiddenBy && this._hiddenBy !== "user") || .disabled=${(this._hiddenBy && this._hiddenBy !== "user") ||
Boolean(this._device?.disabled_by) || Boolean(this._device?.disabled_by) ||
(this._disabledBy && this._disabledBy !== "user")} (this._disabledBy &&
this._disabledBy !== "user" &&
this._disabledBy !== "integration")}
@change=${this._viewStatusChanged} @change=${this._viewStatusChanged}
></ha-radio> ></ha-radio>
</mwc-formfield> </mwc-formfield>
@@ -378,7 +435,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
class="warning" class="warning"
@click=${this._confirmDeleteEntry} @click=${this._confirmDeleteEntry}
.disabled=${this._submitting || .disabled=${this._submitting ||
(!this._helperConfigEntry && !stateObj.attributes.restored)} (!this._helperConfigEntry && !stateObj?.attributes.restored)}
> >
${this.hass.localize("ui.dialogs.entity_registry.editor.delete")} ${this.hass.localize("ui.dialogs.entity_registry.editor.delete")}
</mwc-button> </mwc-button>
@@ -577,6 +634,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
margin: 8px 0; margin: 8px 0;
width: 340px; width: 340px;
} }
li[divider] {
border-bottom-color: var(--divider-color);
}
`, `,
]; ];
} }

View File

@@ -116,7 +116,7 @@ export class HuiCardOptions extends LitElement {
outline: 2px solid var(--primary-color); outline: 2px solid var(--primary-color);
} }
:host:not(.panel) ::slotted(*) { :host(:not(.panel)) ::slotted(*) {
display: block; display: block;
} }

View File

@@ -23,7 +23,6 @@ export const configElementStyle = css`
.suffix { .suffix {
margin: 0 8px; margin: 0 8px;
} }
hui-theme-select-editor,
hui-action-editor, hui-action-editor,
ha-select, ha-select,
ha-textfield, ha-textfield,

View File

@@ -99,6 +99,14 @@ export class HuiAlarmPanelCardEditor
return this.hass!.localize(`ui.panel.lovelace.editor.card.generic.name`); return this.hass!.localize(`ui.panel.lovelace.editor.card.generic.name`);
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.alarm-panel.${ `ui.panel.lovelace.editor.card.alarm-panel.${
schema.name === "states" ? "available_states" : schema.name schema.name === "states" ? "available_states" : schema.name

View File

@@ -69,6 +69,12 @@ export class HuiAreaCardEditor
private _computeLabelCallback = (schema: HaFormSchema) => { private _computeLabelCallback = (schema: HaFormSchema) => {
switch (schema.name) { switch (schema.name) {
case "theme":
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
case "area": case "area":
return this.hass!.localize("ui.panel.lovelace.editor.card.area.name"); return this.hass!.localize("ui.panel.lovelace.editor.card.area.name");
case "navigation_path": case "navigation_path":

View File

@@ -200,6 +200,14 @@ export class HuiButtonCardEditor
)}`; )}`;
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}` `ui.panel.lovelace.editor.card.generic.${schema.name}`
); );

View File

@@ -121,6 +121,14 @@ export class HuiCalendarCardEditor
return this.hass!.localize("ui.panel.lovelace.editor.card.generic.title"); return this.hass!.localize("ui.panel.lovelace.editor.card.generic.title");
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.calendar.${schema.name}` `ui.panel.lovelace.editor.card.calendar.${schema.name}`
); );

View File

@@ -31,7 +31,7 @@ import "../../../../components/ha-icon";
import "../../../../components/ha-switch"; import "../../../../components/ha-switch";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import type { EntitiesCardConfig } from "../../cards/types"; import type { EntitiesCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor"; import "../../../../components/ha-theme-picker";
import { TIMESTAMP_RENDERING_FORMATS } from "../../components/types"; import { TIMESTAMP_RENDERING_FORMATS } from "../../components/types";
import type { LovelaceRowConfig } from "../../entity-rows/types"; import type { LovelaceRowConfig } from "../../entity-rows/types";
import { headerFooterConfigStructs } from "../../header-footer/structs"; import { headerFooterConfigStructs } from "../../header-footer/structs";
@@ -265,12 +265,17 @@ export class HuiEntitiesCardEditor
.configValue=${"title"} .configValue=${"title"}
@input=${this._valueChanged} @input=${this._valueChanged}
></ha-textfield> ></ha-textfield>
<hui-theme-select-editor <ha-theme-picker
.hass=${this.hass} .hass=${this.hass}
.value=${this._theme} .value=${this._theme}
.label=${`${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`}
.configValue=${"theme"} .configValue=${"theme"}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></hui-theme-select-editor> ></ha-theme-picker>
<div class="side-by-side"> <div class="side-by-side">
<ha-formfield <ha-formfield
.label=${this.hass.localize( .label=${this.hass.localize(

View File

@@ -112,6 +112,14 @@ export class HuiEntityCardEditor
); );
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}` `ui.panel.lovelace.editor.card.generic.${schema.name}`
); );

View File

@@ -172,6 +172,12 @@ export class HuiGaugeCardEditor
return this.hass!.localize( return this.hass!.localize(
"ui.panel.lovelace.editor.card.gauge.needle_gauge" "ui.panel.lovelace.editor.card.gauge.needle_gauge"
); );
case "theme":
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
} }
return ( return (
this.hass!.localize( this.hass!.localize(

View File

@@ -117,11 +117,23 @@ export class HuiGlanceCardEditor
fireEvent(this, "config-changed", { config }); fireEvent(this, "config-changed", { config });
} }
private _computeLabelCallback = (schema: HaFormSchema) => private _computeLabelCallback = (schema: HaFormSchema) => {
this.hass!.localize( if (schema.name === "theme") {
`ui.panel.lovelace.editor.card.glance.${schema.name}` return `${this.hass!.localize(
) || "ui.panel.lovelace.editor.card.generic.theme"
this.hass!.localize(`ui.panel.lovelace.editor.card.generic.${schema.name}`); )} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return (
this.hass!.localize(
`ui.panel.lovelace.editor.card.glance.${schema.name}`
) ||
this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
)
);
};
} }
declare global { declare global {

View File

@@ -75,6 +75,14 @@ export class HuiHumidifierCardEditor
); );
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}` `ui.panel.lovelace.editor.card.generic.${schema.name}`
); );

View File

@@ -179,6 +179,14 @@ export class HuiLightCardEditor
); );
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}` `ui.panel.lovelace.editor.card.generic.${schema.name}`
); );

View File

@@ -96,11 +96,24 @@ export class HuiLogbookCardEditor
fireEvent(this, "config-changed", { config: ev.detail.value }); fireEvent(this, "config-changed", { config: ev.detail.value });
} }
private _computeLabelCallback = (schema: HaFormSchema) => private _computeLabelCallback = (schema: HaFormSchema) => {
this.hass!.localize( if (schema.name === "theme") {
`ui.panel.lovelace.editor.card.generic.${schema.name}` return `${this.hass!.localize(
) || "ui.panel.lovelace.editor.card.generic.theme"
this.hass!.localize(`ui.panel.lovelace.editor.card.logbook.${schema.name}`); )} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return (
this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
) ||
this.hass!.localize(
`ui.panel.lovelace.editor.card.logbook.${schema.name}`
)
);
};
} }
declare global { declare global {

View File

@@ -58,13 +58,23 @@ export class HuiMarkdownCardEditor
fireEvent(this, "config-changed", { config: ev.detail.value }); fireEvent(this, "config-changed", { config: ev.detail.value });
} }
private _computeLabelCallback = (schema: HaFormSchema) => private _computeLabelCallback = (schema: HaFormSchema) => {
this.hass!.localize( if (schema.name === "theme") {
`ui.panel.lovelace.editor.card.generic.${schema.name}` return `${this.hass!.localize(
) || "ui.panel.lovelace.editor.card.generic.theme"
this.hass!.localize( )} (${this.hass!.localize(
`ui.panel.lovelace.editor.card.markdown.${schema.name}` "ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return (
this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
) ||
this.hass!.localize(
`ui.panel.lovelace.editor.card.markdown.${schema.name}`
)
); );
};
} }
declare global { declare global {

View File

@@ -5,7 +5,7 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entity-picker"; import "../../../../components/entity/ha-entity-picker";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { MediaControlCardConfig } from "../../cards/types"; import { MediaControlCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor"; import "../../../../components/ha-theme-picker";
import { LovelaceCardEditor } from "../../types"; import { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget, EntitiesEditorEvent } from "../types"; import { EditorTarget, EntitiesEditorEvent } from "../types";
@@ -62,12 +62,17 @@ export class HuiMediaControlCardEditor
@change=${this._valueChanged} @change=${this._valueChanged}
allow-custom-entity allow-custom-entity
></ha-entity-picker> ></ha-entity-picker>
<hui-theme-select-editor <ha-theme-picker
.label=${`${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`}
.hass=${this.hass} .hass=${this.hass}
.value=${this._theme} .value=${this._theme}
.configValue=${"theme"} .configValue=${"theme"}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></hui-theme-select-editor> ></ha-theme-picker>
</div> </div>
`; `;
} }

View File

@@ -6,7 +6,7 @@ import { ActionConfig } from "../../../../data/lovelace";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { PictureCardConfig } from "../../cards/types"; import { PictureCardConfig } from "../../cards/types";
import "../../components/hui-action-editor"; import "../../components/hui-action-editor";
import "../../components/hui-theme-select-editor"; import "../../../../components/ha-theme-picker";
import { LovelaceCardEditor } from "../../types"; import { LovelaceCardEditor } from "../../types";
import { actionConfigStruct } from "../structs/action-struct"; import { actionConfigStruct } from "../structs/action-struct";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
@@ -72,12 +72,17 @@ export class HuiPictureCardEditor
.configValue=${"image"} .configValue=${"image"}
@input=${this._valueChanged} @input=${this._valueChanged}
></ha-textfield> ></ha-textfield>
<hui-theme-select-editor <ha-theme-picker
.hass=${this.hass} .hass=${this.hass}
.value=${this._theme} .value=${this._theme}
.label=${`${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`}
.configValue=${"theme"} .configValue=${"theme"}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></hui-theme-select-editor> ></ha-theme-picker>
<hui-action-editor <hui-action-editor
.label="${this.hass.localize( .label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.tap_action" "ui.panel.lovelace.editor.card.generic.tap_action"

View File

@@ -170,6 +170,14 @@ export class HuiPictureEntityCardEditor
); );
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return ( return (
this.hass!.localize( this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}` `ui.panel.lovelace.editor.card.generic.${schema.name}`

View File

@@ -165,6 +165,14 @@ export class HuiPictureGlanceCardEditor
); );
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return ( return (
this.hass!.localize( this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}` `ui.panel.lovelace.editor.card.generic.${schema.name}`

View File

@@ -64,7 +64,13 @@ export class HuiPlantStatusCardEditor
"ui.panel.lovelace.editor.card.generic.entity" "ui.panel.lovelace.editor.card.generic.entity"
); );
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}` `ui.panel.lovelace.editor.card.generic.${schema.name}`
); );

View File

@@ -153,6 +153,14 @@ export class HuiSensorCardEditor
); );
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
if (schema.name === "detail") { if (schema.name === "detail") {
return this.hass!.localize( return this.hass!.localize(
"ui.panel.lovelace.editor.card.sensor.show_more_detail" "ui.panel.lovelace.editor.card.sensor.show_more_detail"

View File

@@ -6,7 +6,7 @@ import { isComponentLoaded } from "../../../../common/config/is_component_loaded
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { ShoppingListCardConfig } from "../../cards/types"; import { ShoppingListCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor"; import "../../../../components/ha-theme-picker";
import { LovelaceCardEditor } from "../../types"; import { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget, EntitiesEditorEvent } from "../types"; import { EditorTarget, EntitiesEditorEvent } from "../types";
@@ -67,12 +67,17 @@ export class HuiShoppingListEditor
.configValue=${"title"} .configValue=${"title"}
@input=${this._valueChanged} @input=${this._valueChanged}
></ha-textfield> ></ha-textfield>
<hui-theme-select-editor <ha-theme-picker
.hass=${this.hass} .hass=${this.hass}
.value=${this._theme} .value=${this._theme}
.configValue=${"theme"} .configValue=${"theme"}
.label=${`${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></hui-theme-select-editor> ></ha-theme-picker>
</div> </div>
`; `;
} }

View File

@@ -71,6 +71,14 @@ export class HuiThermostatCardEditor
); );
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}` `ui.panel.lovelace.editor.card.generic.${schema.name}`
); );

View File

@@ -184,6 +184,14 @@ export class HuiWeatherForecastCardEditor
)})`; )})`;
} }
if (schema.name === "theme") {
return `${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.theme"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})`;
}
return ( return (
this.hass!.localize( this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}` `ui.panel.lovelace.editor.card.generic.${schema.name}`

View File

@@ -401,6 +401,10 @@
"add_device_id": "Choose device", "add_device_id": "Choose device",
"add_entity_id": "Choose entity" "add_entity_id": "Choose entity"
}, },
"theme-picker": {
"theme": "Theme",
"no_theme": "No theme"
},
"user-picker": { "user-picker": {
"no_user": "No user", "no_user": "No user",
"add_user": "Add user", "add_user": "Add user",
@@ -786,7 +790,31 @@
"door": "Door", "door": "Door",
"garage_door": "Garage door", "garage_door": "Garage door",
"window": "Window", "window": "Window",
"opening": "Other" "opening": "Opening",
"battery": "Battery",
"battery_charging": "Battery charging",
"carbon_monoxide": "Carbon monoxide",
"cold": "Cold",
"connectivity": "Connectivity",
"gas": "Gas",
"heat": "Heat",
"light": "Light",
"lock": "Lock",
"moisture": "Moisture",
"motion": "Motion",
"moving": "Moving",
"occupancy": "Occupancy",
"plug": "Plug",
"power": "Power",
"presence": "Presence",
"problem": "Problem",
"running": "Running",
"safety": "Safety",
"smoke": "Smoke",
"sound": "Sound",
"tamper": "Tamper",
"update": "Update",
"vibration": "Vibration"
}, },
"cover": { "cover": {
"door": "Door", "door": "Door",
@@ -3458,7 +3486,6 @@
"tap_action": "Tap Action", "tap_action": "Tap Action",
"title": "Title", "title": "Title",
"theme": "Theme", "theme": "Theme",
"no_theme": "No theme",
"unit": "Unit", "unit": "Unit",
"url": "URL", "url": "URL",
"state": "State", "state": "State",