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 */
import "@material/mwc-button";
import { LitElement, TemplateResult, html } from "lit";
import { html, LitElement, TemplateResult } from "lit";
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 { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
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 { HomeAssistant } from "../../../../src/types";
import { getEntity } from "../../../../src/fake_data/entity";
import "../../components/demo-black-white-row";
const ENTITIES = [
getEntity("alarm_control_panel", "alarm", "disarmed", {
@@ -147,7 +147,9 @@ const SCHEMAS: {
{ name: "target", selector: { target: {} } },
{ name: "number", selector: { number: { min: 0, max: 10 } } },
{ 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: "text", selector: { text: { multiline: false } } },
{ name: "text_multiline", selector: { text: { multiline: true } } },

View File

@@ -1,20 +1,20 @@
/* eslint-disable lit/no-template-arrow */
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 { 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-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 { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin";
import type { HomeAssistant } from "../../../../src/types";
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 = [
getEntity("alarm_control_panel", "alarm", "disarmed", {
@@ -202,6 +202,7 @@ const SCHEMAS: {
input: {
entity: { name: "Entity", selector: { entity: { multiple: true } } },
device: { name: "Device", selector: { device: { multiple: true } } },
area: { name: "Area", selector: { area: { multiple: true } } },
},
},
];

View File

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

View File

@@ -18,7 +18,6 @@ import "./state-badge";
interface HassEntityWithCachedName extends HassEntity {
friendly_name: string;
id: string;
}
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
@@ -97,9 +96,6 @@ export class HaEntityPicker extends LitElement {
@property() public entityFilter?: HaEntityPickerEntityFilterFunc;
@property({ attribute: "item-value-path" }) public itemValuePath =
"entity_id";
@property({ type: Boolean }) public hideClearIcon = false;
@state() private _opened = false;
@@ -148,7 +144,6 @@ export class HaEntityPicker extends LitElement {
state: "",
last_changed: "",
last_updated: "",
id: "",
context: { id: "", user_id: null, parent_id: null },
friendly_name: this.hass!.localize(
"ui.components.entity.entity-picker.no_entities"
@@ -169,15 +164,10 @@ export class HaEntityPicker extends LitElement {
);
return entityIds
.map((key) => {
const stateObj = hass!.states[key];
return {
...stateObj,
friendly_name: computeStateName(stateObj) || key,
id: stateObj.context.id,
};
})
.map((key) => ({
...hass!.states[key],
friendly_name: computeStateName(hass!.states[key]) || key,
}))
.sort((entityA, entityB) =>
caseInsensitiveStringCompare(
entityA.friendly_name,
@@ -205,15 +195,10 @@ export class HaEntityPicker extends LitElement {
}
states = entityIds
.map((key) => {
const stateObj = hass!.states[key];
return {
...stateObj,
friendly_name: computeStateName(stateObj) || key,
id: stateObj.context?.id,
};
})
.map((key) => ({
...hass!.states[key],
friendly_name: computeStateName(hass!.states[key]) || key,
}))
.sort((entityA, entityB) =>
caseInsensitiveStringCompare(
entityA.friendly_name,
@@ -258,7 +243,6 @@ export class HaEntityPicker extends LitElement {
state: "",
last_changed: "",
last_updated: "",
id: "",
context: { id: "", user_id: null, parent_id: null },
friendly_name: this.hass!.localize(
"ui.components.entity.entity-picker.no_match"
@@ -311,8 +295,8 @@ export class HaEntityPicker extends LitElement {
protected render(): TemplateResult {
return html`
<ha-combo-box
item-value-path="entity_id"
item-label-path="friendly_name"
.itemValuePath=${this.itemValuePath}
.hass=${this.hass}
.value=${this._value}
.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 "./ha-textfield";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation";
import "./ha-select";
import "./ha-textfield";
export interface TimeChangedEvent {
days?: number;
hours: number;
minutes: number;
seconds: number;
@@ -21,6 +22,11 @@ export class HaBaseTimeInput extends LitElement {
*/
@property() label?: string;
/**
* Helper for the input
*/
@property() helper?: string;
/**
* auto validate time inputs
*/
@@ -41,6 +47,11 @@ export class HaBaseTimeInput extends LitElement {
*/
@property({ type: Boolean }) disabled = false;
/**
* day
*/
@property({ type: Number }) days = 0;
/**
* hour
*/
@@ -61,6 +72,11 @@ export class HaBaseTimeInput extends LitElement {
*/
@property({ type: Number }) milliseconds = 0;
/**
* Label for the day input
*/
@property() dayLabel = "";
/**
* Label for the hour input
*/
@@ -91,6 +107,11 @@ export class HaBaseTimeInput extends LitElement {
*/
@property({ type: Boolean }) enableMillisecond = false;
/**
* show the day field
*/
@property({ type: Boolean }) enableDay = false;
/**
* limit hours input
*/
@@ -110,6 +131,29 @@ export class HaBaseTimeInput extends LitElement {
return html`
${this.label ? html`<label>${this.label}</label>` : ""}
<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
id="hour"
type="number"
@@ -207,6 +251,7 @@ export class HaBaseTimeInput extends LitElement {
<mwc-list-item value="PM">PM</mwc-list-item>
</ha-select>`}
</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));
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";
export interface HaDurationData {
days?: number;
hours?: number;
minutes?: number;
seconds?: number;
@@ -17,10 +18,14 @@ class HaDurationInput extends LitElement {
@property() public label?: string;
@property() public helper?: string;
@property({ type: Boolean }) public required?: boolean;
@property({ type: Boolean }) public enableMillisecond?: boolean;
@property({ type: Boolean }) public enableDay?: boolean;
@property({ type: Boolean }) public disabled = false;
@query("paper-time-input", true) private _input?: HTMLElement;
@@ -35,19 +40,23 @@ class HaDurationInput extends LitElement {
return html`
<ha-base-time-input
.label=${this.label}
.helper=${this.helper}
.required=${this.required}
.autoValidate=${this.required}
.disabled=${this.disabled}
errorMessage="Required"
enableSecond
.enableMillisecond=${this.enableMillisecond}
.enableDay=${this.enableDay}
format="24"
.days=${this._days}
.hours=${this._hours}
.minutes=${this._minutes}
.seconds=${this._seconds}
.milliseconds=${this._milliseconds}
@value-changed=${this._durationChanged}
noHoursLimit
dayLabel="dd"
hourLabel="hh"
minLabel="mm"
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() {
return this.data?.hours ? Number(this.data.hours) : 0;
}
@@ -94,6 +107,11 @@ class HaDurationInput extends LitElement {
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", {
value,
});

View File

@@ -34,12 +34,25 @@ export const computeInitialHaFormData = (
};
} else if ("selector" in field) {
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;
} 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] = "";
} 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) {
if (selector.select.options.length) {
data[field.name] = selector.select.options[0][0];
@@ -50,6 +63,23 @@ export const computeInitialHaFormData = (
minutes: 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 helper?: string;
@property({ type: Boolean }) public disabled = false;
@state() private _unmaskedPassword = false;
@@ -53,6 +55,8 @@ export class HaFormString extends LitElement implements HaFormElement {
: "password"}
.label=${this.label}
.value=${this.data || ""}
.helper=${this.helper}
helperPersistent
.disabled=${this.disabled}
.required=${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 { HomeAssistant } from "../../types";
import "../ha-area-picker";
import "../ha-areas-picker";
@customElement("ha-selector-area")
export class HaAreaSelector extends LitElement {
@@ -38,21 +39,43 @@ export class HaAreaSelector extends LitElement {
}
protected render() {
return html`<ha-area-picker
.hass=${this.hass}
.value=${this.value}
.label=${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-area-picker>`;
if (!this.selector.area.multiple) {
return html`
<ha-area-picker
.hass=${this.hass}
.value=${this.value}
.label=${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-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 => {

View File

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

View File

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

View File

@@ -6,7 +6,6 @@ import type {
LocationSelector,
LocationSelectorValue,
} from "../../data/selector";
import "../../panels/lovelace/components/hui-theme-select-editor";
import type { HomeAssistant } from "../../types";
import type { MarkerLocation } from "../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,
radius: selector.location.radius ? value?.radius || 1000 : undefined,
radius_color: zoneRadiusColor,
icon: selector.location.icon,
icon:
selector.location.icon || selector.location.radius
? "mdi:map-marker-radius"
: "mdi:map-marker",
location_editable: true,
radius_editable: true,
},

View File

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

View File

@@ -18,6 +18,8 @@ export class HaTextSelector extends LitElement {
@property() public placeholder?: string;
@property() public helper?: string;
@property() public selector!: StringSelector;
@property({ type: Boolean }) public disabled = false;
@@ -32,6 +34,8 @@ export class HaTextSelector extends LitElement {
.label=${this.label}
.placeholder=${this.placeholder}
.value=${this.value || ""}
.helper=${this.helper}
helperPersistent
.disabled=${this.disabled}
@input=${this._handleChange}
autocapitalize="none"
@@ -44,6 +48,8 @@ export class HaTextSelector extends LitElement {
return html`<ha-textfield
.value=${this.value || ""}
.placeholder=${this.placeholder || ""}
.helper=${this.helper}
helperPersistent
.disabled=${this.disabled}
.type=${this._unmaskedPassword ? "text" : this.selector.text?.type}
@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 { customElement, property } from "lit/decorators";
import type { HomeAssistant } from "../../types";
@@ -18,11 +18,11 @@ export class HaThemeSelector extends LitElement {
protected render() {
return html`
<hui-theme-select-editor
<ha-theme-picker
.hass=${this.hass}
.value=${this.value}
.label=${this.label}
></hui-theme-select-editor>
></ha-theme-picker>
`;
}
}

View File

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

View File

@@ -21,7 +21,7 @@ export class HaSettingsRow extends LitElement {
<div secondary><slot name="description"></slot></div>
</paper-item-body>
</div>
<slot></slot>
<div class="content"><slot></slot></div>
`;
}
@@ -43,6 +43,18 @@ export class HaSettingsRow extends LitElement {
);
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]) {
align-items: normal;
flex-direction: column;

View File

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

View File

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

View File

@@ -46,6 +46,7 @@ export interface AreaSelector {
manufacturer?: DeviceSelector["device"]["manufacturer"];
model?: DeviceSelector["device"]["model"];
};
multiple?: boolean;
};
}
@@ -96,8 +97,9 @@ export interface DeviceSelector {
}
export interface DurationSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
duration: {};
duration: {
enable_day?: boolean;
};
}
export interface EntitySelector {
@@ -106,7 +108,6 @@ export interface EntitySelector {
domain?: string | string[];
device_class?: string;
multiple?: boolean;
use_uuid?: boolean;
include_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 "../../components/ha-circular-progress";
import "../../components/ha-dialog";
import "../../components/ha-form/ha-form";
import "../../components/ha-icon-button";
import "../../components/ha-markdown";
import {
AreaRegistryEntry,
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) {
return hass.localize(
`component.${step.handler}.config.error.${error}`,

View File

@@ -50,6 +50,12 @@ export interface FlowConfig {
field: HaFormSchema
): string;
renderShowFormStepFieldHelper(
hass: HomeAssistant,
step: DataEntryFlowStepForm,
field: HaFormSchema
): string;
renderShowFormStepFieldError(
hass: HomeAssistant,
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) {
return hass.localize(
`component.${configEntry.domain}.options.error.${error}`,

View File

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

View File

@@ -326,12 +326,9 @@ export class HaBlueprintAutomationEditor extends LitElement {
}
ha-settings-row {
--paper-time-input-justify-content: flex-end;
--settings-row-content-width: 100%;
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-formfield/mwc-formfield";
import "@material/mwc-list/mwc-list-item";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import {
@@ -20,9 +19,15 @@ import "../../../components/ha-alert";
import "../../../components/ha-area-picker";
import "../../../components/ha-expansion-panel";
import "../../../components/ha-icon-picker";
import "../../../components/ha-radio";
import "../../../components/ha-select";
import "../../../components/ha-switch";
import "../../../components/ha-textfield";
import {
ConfigEntry,
deleteConfigEntry,
getConfigEntries,
} from "../../../data/config_entries";
import {
DeviceRegistryEntry,
subscribeDeviceRegistry,
@@ -34,6 +39,7 @@ import {
removeEntityRegistryEntry,
updateEntityRegistryEntry,
} from "../../../data/entity_registry";
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
import {
showAlertDialog,
showConfirmationDialog,
@@ -42,27 +48,39 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
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 = {
cover: [
"awning",
"blind",
"curtain",
"damper",
"door",
"garage",
"gate",
"shade",
"shutter",
"window",
[
"awning",
"blind",
"curtain",
"damper",
"door",
"garage",
"gate",
"shade",
"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")
@@ -85,8 +103,6 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
@state() private _hiddenBy!: string | null;
private _deviceLookup?: Record<string, DeviceRegistryEntry>;
@state() private _device?: DeviceRegistryEntry;
@state() private _helperConfigEntry?: ConfigEntry;
@@ -97,6 +113,10 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
private _origEntityId!: string;
private _deviceLookup?: Record<string, DeviceRegistryEntry>;
private _deviceClassOptions?: string[][];
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
@@ -125,23 +145,41 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
}
}
protected updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
if (changedProperties.has("entry")) {
this._error = undefined;
this._name = this.entry.name || "";
this._icon = this.entry.icon || "";
this._deviceClass =
this.entry.device_class || this.entry.original_device_class;
this._origEntityId = this.entry.entity_id;
this._areaId = this.entry.area_id;
this._entityId = this.entry.entity_id;
this._disabledBy = this.entry.disabled_by;
this._hiddenBy = this.entry.hidden_by;
this._device =
this.entry.device_id && this._deviceLookup
? this._deviceLookup[this.entry.device_id]
: undefined;
protected willUpdate(changedProperties: PropertyValues) {
super.willUpdate(changedProperties);
if (!changedProperties.has("entry")) {
return;
}
this._error = undefined;
this._name = this.entry.name || "";
this._icon = this.entry.icon || "";
this._deviceClass =
this.entry.device_class || this.entry.original_device_class;
this._origEntityId = this.entry.entity_id;
this._areaId = this.entry.area_id;
this._entityId = this.entry.entity_id;
this._disabledBy = this.entry.disabled_by;
this._hiddenBy = this.entry.hidden_by;
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}
.disabled=${this._submitting}
></ha-icon-picker>
${OVERRIDE_DEVICE_CLASSES[domain]?.includes(this._deviceClass) ||
(domain === "cover" && this.entry.original_device_class === null)
? html`<ha-select
.label=${this.hass.localize(
"ui.dialogs.entity_registry.editor.device_class"
)}
.value=${this._deviceClass}
naturalMenuWidth
fixedMenuPosition
@selected=${this._deviceClassChanged}
@closed=${stopPropagation}
>
${OVERRIDE_DEVICE_CLASSES[domain].map(
(deviceClass: string) => html`
<mwc-list-item .value=${deviceClass}>
${this.hass.localize(
`ui.dialogs.entity_registry.editor.device_classes.${domain}.${deviceClass}`
)}
</mwc-list-item>
`
)}
</ha-select>`
${this._deviceClassOptions
? html`
<ha-select
.label=${this.hass.localize(
"ui.dialogs.entity_registry.editor.device_class"
)}
.value=${this._deviceClass}
naturalMenuWidth
fixedMenuPosition
@selected=${this._deviceClassChanged}
@closed=${stopPropagation}
>
${this._deviceClassOptions[0].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>
`
)}
<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
error-message="Domain needs to stay the same"
@@ -264,7 +313,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
)}:
</div>
<div class="secondary">
${this._disabledBy && this._disabledBy !== "user"
${this._disabledBy &&
this._disabledBy !== "user" &&
this._disabledBy !== "integration"
? this.hass.localize(
"ui.dialogs.entity_registry.editor.enabled_cause",
"cause",
@@ -286,7 +337,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
.checked=${!this._hiddenBy && !this._disabledBy}
.disabled=${(this._hiddenBy && this._hiddenBy !== "user") ||
this._device?.disabled_by ||
(this._disabledBy && this._disabledBy !== "user")}
(this._disabledBy &&
this._disabledBy !== "user" &&
this._disabledBy !== "integration")}
@change=${this._viewStatusChanged}
></ha-radio>
</mwc-formfield>
@@ -301,7 +354,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
.checked=${this._hiddenBy !== null}
.disabled=${(this._hiddenBy && this._hiddenBy !== "user") ||
Boolean(this._device?.disabled_by) ||
(this._disabledBy && this._disabledBy !== "user")}
(this._disabledBy &&
this._disabledBy !== "user" &&
this._disabledBy !== "integration")}
@change=${this._viewStatusChanged}
></ha-radio>
</mwc-formfield>
@@ -316,7 +371,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
.checked=${this._disabledBy !== null}
.disabled=${(this._hiddenBy && this._hiddenBy !== "user") ||
Boolean(this._device?.disabled_by) ||
(this._disabledBy && this._disabledBy !== "user")}
(this._disabledBy &&
this._disabledBy !== "user" &&
this._disabledBy !== "integration")}
@change=${this._viewStatusChanged}
></ha-radio>
</mwc-formfield>
@@ -378,7 +435,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
class="warning"
@click=${this._confirmDeleteEntry}
.disabled=${this._submitting ||
(!this._helperConfigEntry && !stateObj.attributes.restored)}
(!this._helperConfigEntry && !stateObj?.attributes.restored)}
>
${this.hass.localize("ui.dialogs.entity_registry.editor.delete")}
</mwc-button>
@@ -577,6 +634,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
margin: 8px 0;
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);
}
:host:not(.panel) ::slotted(*) {
:host(:not(.panel)) ::slotted(*) {
display: block;
}

View File

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

View File

@@ -99,6 +99,14 @@ export class HuiAlarmPanelCardEditor
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(
`ui.panel.lovelace.editor.card.alarm-panel.${
schema.name === "states" ? "available_states" : schema.name

View File

@@ -69,6 +69,12 @@ export class HuiAreaCardEditor
private _computeLabelCallback = (schema: HaFormSchema) => {
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":
return this.hass!.localize("ui.panel.lovelace.editor.card.area.name");
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(
`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");
}
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(
`ui.panel.lovelace.editor.card.calendar.${schema.name}`
);

View File

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

View File

@@ -172,6 +172,12 @@ export class HuiGaugeCardEditor
return this.hass!.localize(
"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 (
this.hass!.localize(

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entity-picker";
import { HomeAssistant } from "../../../../types";
import { MediaControlCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
import "../../../../components/ha-theme-picker";
import { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget, EntitiesEditorEvent } from "../types";
@@ -62,12 +62,17 @@ export class HuiMediaControlCardEditor
@change=${this._valueChanged}
allow-custom-entity
></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}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
></ha-theme-picker>
</div>
`;
}

View File

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

View File

@@ -64,7 +64,13 @@ export class HuiPlantStatusCardEditor
"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(
`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") {
return this.hass!.localize(
"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 { HomeAssistant } from "../../../../types";
import { ShoppingListCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
import "../../../../components/ha-theme-picker";
import { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget, EntitiesEditorEvent } from "../types";
@@ -67,12 +67,17 @@ export class HuiShoppingListEditor
.configValue=${"title"}
@input=${this._valueChanged}
></ha-textfield>
<hui-theme-select-editor
<ha-theme-picker
.hass=${this.hass}
.value=${this._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}
></hui-theme-select-editor>
></ha-theme-picker>
</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(
`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 (
this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`

View File

@@ -401,6 +401,10 @@
"add_device_id": "Choose device",
"add_entity_id": "Choose entity"
},
"theme-picker": {
"theme": "Theme",
"no_theme": "No theme"
},
"user-picker": {
"no_user": "No user",
"add_user": "Add user",
@@ -786,7 +790,31 @@
"door": "Door",
"garage_door": "Garage door",
"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": {
"door": "Door",
@@ -3458,7 +3486,6 @@
"tap_action": "Tap Action",
"title": "Title",
"theme": "Theme",
"no_theme": "No theme",
"unit": "Unit",
"url": "URL",
"state": "State",