Compare commits

..

25 Commits

Author SHA1 Message Date
Zack Barett
d3765987c4 Merge branch 'dev' into Better-row-editor 2020-11-30 20:07:26 -06:00
Zack Arnett
259cb6a2d2 Review updates 2020-11-30 20:03:54 -06:00
Zack Arnett
68c3825c7b lint 2020-11-20 12:24:06 -06:00
Zack Arnett
2e3c3ded96 Update the rest of the editors 2020-11-20 12:19:08 -06:00
Zack Arnett
b6a93b439e more lint 2020-11-19 19:32:27 -06:00
Zack Arnett
ea4a5e4f1d lint 2020-11-19 19:24:27 -06:00
Zack Arnett
de71c348a5 Add More cards editors to config template 2020-11-19 19:20:31 -06:00
Zack Arnett
b27fe7e703 Clean up 2020-11-18 20:29:04 -06:00
Zack Arnett
06db9c4bee Show Code editor to only advanced 2020-11-18 20:16:53 -06:00
Zack Arnett
ae5018b501 lint 2020-11-18 18:13:56 -06:00
Zack Arnett
9ef3d02636 Fix header footer editor 2020-11-18 17:05:23 -06:00
Zack Arnett
9f2723deb8 fix enlarge 2020-11-18 17:02:02 -06:00
Zack Arnett
e9845e1009 Add row do something at least 2020-11-18 16:57:08 -06:00
Zack Arnett
40d8a76b4d Update Header footer 2020-11-17 22:23:41 -06:00
Zack Arnett
7a282ede59 Fix menu 2020-11-17 19:55:15 -06:00
Zack Arnett
48455c767f lint and wording 2020-11-17 17:22:01 -06:00
Zack Arnett
4385dd5c44 Convert entities card to advanced template 2020-11-17 17:17:30 -06:00
Zack Arnett
7733a5f831 Add handle back 2020-11-17 16:35:50 -06:00
Zack Arnett
b6c9676930 Lint 2020-11-17 16:18:21 -06:00
Zack Arnett
3692e36f12 use ha-settings-row 2020-11-17 16:13:15 -06:00
Zack Arnett
30e5fdb6e5 Advancded 2020-11-17 11:52:08 -06:00
Zack Arnett
99f4afec8d console.die 2020-11-15 16:25:30 -06:00
Zack Arnett
5ae6c05fe6 Update scrollbar on Dialog 2020-11-15 15:29:29 -06:00
Zack Arnett
effd5e4982 More changes 2020-11-15 14:50:00 -06:00
Zack Arnett
5592ea80a9 Make this place a little bit better 2020-11-14 20:46:14 -06:00
95 changed files with 2388 additions and 3830 deletions

View File

@@ -7,8 +7,8 @@ export const createMediaPlayerEntities = () => [
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 64063,
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
supported_features: 195135,
entity_picture: "/images/album_cover_2.jpg",
media_duration: 300,
media_position: 50,
@@ -24,8 +24,8 @@ export const createMediaPlayerEntities = () => [
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
supported_features: 195135,
// Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 64063,
entity_picture: "/images/album_cover.jpg",
media_duration: 300,
media_position: 0,

View File

@@ -146,16 +146,6 @@ const CONFIGS = [
entity: media_player.receiver_off
`,
},
{
heading: "Grid Full Size",
config: `
- type: grid
columns: 1
cards:
- type: media-control
entity: media_player.music_paused
`,
},
];
class DemoHuiMediControlCard extends PolymerElement {

View File

@@ -74,7 +74,9 @@ export class HassioUpdate extends LitElement {
"Supervisor",
this.supervisor.supervisor,
"hassio/supervisor/update",
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}`
`https://github.com//home-assistant/hassio/releases/tag/${
this.supervisor.supervisor.version_latest
}`
)}
${this.supervisor.host.features.includes("hassos")
? this._renderUpdateCard(

View File

@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="home-assistant-frontend",
version="20201202.0",
version="20201126.0",
description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors",

View File

@@ -1,6 +0,0 @@
export const ensureArray = (value?: any) => {
if (!value || Array.isArray(value)) {
return value;
}
return [value];
};

View File

@@ -67,10 +67,6 @@ export const computeStateDisplay = (
}
}
if (domain === "counter") {
return formatNumber(compareState, language);
}
return (
// Return device class translation
(stateObj.attributes.device_class &&

View File

@@ -139,7 +139,7 @@ export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
private _filteredDevices: DeviceRegistryEntry[] = [];
private _getAreasWithDevices = memoizeOne(
private _getDevices = memoizeOne(
(
devices: DeviceRegistryEntry[],
areas: AreaRegistryEntry[],
@@ -277,7 +277,7 @@ export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
if (!this._devices || !this._areas || !this._entities) {
return html``;
}
const areas = this._getAreasWithDevices(
const areas = this._getDevices(
this._devices,
this._areas,
this._entities,

View File

@@ -111,18 +111,6 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
@property({ type: Boolean })
private _opened?: boolean;
public open() {
this.updateComplete.then(() => {
(this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
});
}
public focus() {
this.updateComplete.then(() => {
this.shadowRoot?.querySelector("paper-input")?.focus();
});
}
private _getDevices = memoizeOne(
(
devices: DeviceRegistryEntry[],
@@ -138,17 +126,14 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
}
const deviceEntityLookup: DeviceEntityLookup = {};
if (includeDomains || excludeDomains || includeDeviceClasses) {
for (const entity of entities) {
if (!entity.device_id) {
continue;
}
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
for (const entity of entities) {
if (!entity.device_id) {
continue;
}
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
}
const areaLookup: { [areaId: string]: AreaRegistryEntry } = {};
@@ -156,9 +141,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
areaLookup[area.area_id] = area;
}
let inputDevices = devices.filter(
(device) => device.id === this.value || !device.disabled_by
);
let inputDevices = [...devices];
if (includeDomains) {
inputDevices = inputDevices.filter((device) => {

View File

@@ -95,24 +95,13 @@ export class HaEntityPicker extends LitElement {
@property() public entityFilter?: HaEntityPickerEntityFilterFunc;
@property({ type: Boolean }) public hideClearIcon = false;
@property({ type: Boolean, attribute: "hide-clear-icon" })
public hideClearIcon = false;
@property({ type: Boolean }) private _opened = false;
@query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
public open() {
this.updateComplete.then(() => {
(this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
});
}
public focus() {
this.updateComplete.then(() => {
this.shadowRoot?.querySelector("paper-input")?.focus();
});
}
private _initedStates = false;
private _states: HassEntity[] = [];

View File

@@ -29,17 +29,6 @@ import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { PolymerChangedEvent } from "../polymer-types";
import { HomeAssistant } from "../types";
import memoizeOne from "memoize-one";
import {
DeviceEntityLookup,
DeviceRegistryEntry,
subscribeDeviceRegistry,
} from "../data/device_registry";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../data/entity_registry";
import { computeDomain } from "../common/entity/compute_domain";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
const rowRenderer = (
root: HTMLElement,
@@ -82,225 +71,39 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
@property() public placeholder?: string;
@property() public _areas?: AreaRegistryEntry[];
@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;
@internalProperty() private _areas?: AreaRegistryEntry[];
@internalProperty() private _devices?: DeviceRegistryEntry[];
@internalProperty() private _entities?: EntityRegistryEntry[];
@internalProperty() private _opened?: boolean;
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeAreaRegistry(this.hass.connection!, (areas) => {
this._areas = areas;
}),
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
this._devices = devices;
}),
subscribeEntityRegistry(this.hass.connection!, (entities) => {
this._entities = entities;
this._areas = this.noAdd
? areas
: [
...areas,
{
area_id: "add_new",
name: this.hass.localize("ui.components.area-picker.add_new"),
},
];
}),
];
}
public open() {
this.updateComplete.then(() => {
(this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
});
}
public focus() {
this.updateComplete.then(() => {
this.shadowRoot?.querySelector("paper-input")?.focus();
});
}
private _getAreas = memoizeOne(
(
areas: AreaRegistryEntry[],
devices: DeviceRegistryEntry[],
entities: EntityRegistryEntry[],
includeDomains: this["includeDomains"],
excludeDomains: this["excludeDomains"],
includeDeviceClasses: this["includeDeviceClasses"],
deviceFilter: this["deviceFilter"],
entityFilter: this["entityFilter"],
noAdd: this["noAdd"]
): AreaRegistryEntry[] => {
const deviceEntityLookup: DeviceEntityLookup = {};
let inputDevices: DeviceRegistryEntry[] | undefined;
let inputEntities: EntityRegistryEntry[] | undefined;
if (includeDomains || excludeDomains || includeDeviceClasses) {
for (const entity of entities) {
if (!entity.device_id) {
continue;
}
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
}
inputDevices = devices;
inputEntities = entities.filter((entity) => entity.area_id);
} else if (deviceFilter) {
inputDevices = devices;
} else if (entityFilter) {
inputEntities = entities.filter((entity) => entity.area_id);
}
if (includeDomains) {
inputDevices = inputDevices!.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return false;
}
return deviceEntityLookup[device.id].some((entity) =>
includeDomains.includes(computeDomain(entity.entity_id))
);
});
inputEntities = inputEntities!.filter((entity) =>
includeDomains.includes(computeDomain(entity.entity_id))
);
}
if (excludeDomains) {
inputDevices = inputDevices!.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return true;
}
return entities.every(
(entity) =>
!excludeDomains.includes(computeDomain(entity.entity_id))
);
});
inputEntities = inputEntities!.filter(
(entity) => !excludeDomains.includes(computeDomain(entity.entity_id))
);
}
if (includeDeviceClasses) {
inputDevices = inputDevices!.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return false;
}
return deviceEntityLookup[device.id].some((entity) => {
const stateObj = this.hass.states[entity.entity_id];
if (!stateObj) {
return false;
}
return (
stateObj.attributes.device_class &&
includeDeviceClasses.includes(stateObj.attributes.device_class)
);
});
});
inputEntities = inputEntities!.filter((entity) => {
const stateObj = this.hass.states[entity.entity_id];
return (
stateObj.attributes.device_class &&
includeDeviceClasses.includes(stateObj.attributes.device_class)
);
});
}
if (deviceFilter) {
inputDevices = inputDevices!.filter((device) => deviceFilter!(device));
}
if (entityFilter) {
entities = entities.filter((entity) => entityFilter!(entity));
}
let outputAreas = areas;
let areaIds: string[] | undefined;
if (inputDevices) {
areaIds = inputDevices
.filter((device) => device.area_id)
.map((device) => device.area_id!);
}
if (inputEntities) {
areaIds = (areaIds ?? []).concat(
inputEntities
.filter((entity) => entity.area_id)
.map((entity) => entity.area_id!)
);
}
if (areaIds) {
outputAreas = areas.filter((area) => areaIds!.includes(area.area_id));
}
return noAdd
? outputAreas
: [
...outputAreas,
{
area_id: "add_new",
name: this.hass.localize("ui.components.area-picker.add_new"),
},
];
}
);
protected render(): TemplateResult {
if (!this._devices || !this._areas || !this._entities) {
if (!this._areas) {
return html``;
}
const areas = this._getAreas(
this._areas,
this._devices,
this._entities,
this.includeDomains,
this.excludeDomains,
this.includeDeviceClasses,
this.deviceFilter,
this.entityFilter,
this.noAdd
);
return html`
<vaadin-combo-box-light
item-value-path="area_id"
item-id-path="area_id"
item-label-path="name"
.items=${areas}
.items=${this._areas}
.value=${this._value}
.renderer=${rowRenderer}
@opened-changed=${this._openedChanged}
@@ -335,7 +138,7 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
</ha-icon-button>
`
: ""}
${areas.length > 0
${this._areas.length > 0
? html`
<ha-icon-button
aria-label=${this.hass.localize(

View File

@@ -23,6 +23,8 @@ export class HaButtonMenu extends LitElement {
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public fixed = false;
@query("mwc-menu", true) private _menu?: Menu;
public get items() {
@@ -39,6 +41,7 @@ export class HaButtonMenu extends LitElement {
<slot name="trigger"></slot>
</div>
<mwc-menu
.fixed=${this.fixed}
.corner=${this.corner}
.multi=${this.multi}
.activatable=${this.activatable}

View File

@@ -11,7 +11,6 @@ import {
import { fireEvent } from "../common/dom/fire_event";
import type { ToggleButton } from "../types";
import "./ha-svg-icon";
import "@material/mwc-button/mwc-button";
@customElement("ha-button-toggle-group")
export class HaButtonToggleGroup extends LitElement {
@@ -22,22 +21,17 @@ export class HaButtonToggleGroup extends LitElement {
protected render(): TemplateResult {
return html`
<div>
${this.buttons.map((button) =>
button.iconPath
? html`<mwc-icon-button
.label=${button.label}
.value=${button.value}
?active=${this.active === button.value}
@click=${this._handleClick}
>
<ha-svg-icon .path=${button.iconPath}></ha-svg-icon>
</mwc-icon-button>`
: html`<mwc-button
.value=${button.value}
?active=${this.active === button.value}
@click=${this._handleClick}
>${button.label}</mwc-button
>`
${this.buttons.map(
(button) => html`
<mwc-icon-button
.label=${button.label}
.value=${button.value}
?active=${this.active === button.value}
@click=${this._handleClick}
>
<ha-svg-icon .path=${button.iconPath}></ha-svg-icon>
</mwc-icon-button>
`
)}
</div>
`;
@@ -55,15 +49,13 @@ export class HaButtonToggleGroup extends LitElement {
--mdc-icon-button-size: var(--button-toggle-size, 36px);
--mdc-icon-size: var(--button-toggle-icon-size, 20px);
}
mwc-icon-button,
mwc-button {
mwc-icon-button {
border: 1px solid var(--primary-color);
border-right-width: 0px;
position: relative;
cursor: pointer;
}
mwc-icon-button::before,
mwc-button::before {
mwc-icon-button::before {
top: 0;
left: 0;
width: 100%;
@@ -75,21 +67,17 @@ export class HaButtonToggleGroup extends LitElement {
content: "";
transition: opacity 15ms linear, background-color 15ms linear;
}
mwc-icon-button[active]::before,
mwc-button[active]::before {
mwc-icon-button[active]::before {
opacity: var(--mdc-icon-button-ripple-opacity, 0.12);
}
mwc-icon-button:first-child,
mwc-button:first-child {
mwc-icon-button:first-child {
border-radius: 4px 0 0 4px;
}
mwc-icon-button:last-child,
mwc-button:last-child {
mwc-icon-button:last-child {
border-radius: 0 4px 4px 0;
border-right-width: 1px;
}
mwc-icon-button:only-child,
mwc-button:only-child {
mwc-icon-button:only-child {
border-radius: 4px;
border-right-width: 1px;
}

View File

@@ -68,6 +68,12 @@ export class HaDialog extends MwcDialog {
top: var(--dialog-surface-top);
min-height: var(--mdc-dialog-min-height, auto);
}
:host([full]) .mdc-dialog .mdc-dialog__surface {
height: 100%;
width: 100%;
}
:host([flexContent]) .mdc-dialog .mdc-dialog__content {
display: flex;
flex-direction: column;
@@ -90,6 +96,23 @@ export class HaDialog extends MwcDialog {
margin-left: 40px;
margin-right: 0px;
}
.mdc-dialog__content::-webkit-scrollbar {
width: 0.4rem;
height: 0.4rem;
}
.mdc-dialog__content::-webkit-scrollbar-thumb {
-webkit-border-radius: 4px;
border-radius: 4px;
background: var(--scrollbar-thumb-color);
}
.mdc-dialog__content {
overflow-y: auto;
scrollbar-color: var(--scrollbar-thumb-color) transparent;
scrollbar-width: thin;
}
`,
];
}

View File

@@ -1,3 +1,4 @@
import { mdiChevronDown } from "@mdi/js";
import {
css,
CSSResult,
@@ -8,10 +9,9 @@ import {
query,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import { fireEvent } from "../common/dom/fire_event";
import "./ha-svg-icon";
import { mdiChevronDown } from "@mdi/js";
import { classMap } from "lit-html/directives/class-map";
@customElement("ha-expansion-panel")
class HaExpansionPanel extends LitElement {

View File

@@ -1,4 +1,4 @@
import { mdiHelpCircle } from "@mdi/js";
import { mdiHelpCircleOutline } from "@mdi/js";
import "@polymer/paper-tooltip/paper-tooltip";
import {
css,
@@ -18,7 +18,7 @@ export class HaHelpTooltip extends LitElement {
protected render(): TemplateResult {
return html`
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
<ha-svg-icon .path=${mdiHelpCircleOutline}></ha-svg-icon>
<paper-tooltip
offset="4"
.position=${this.position}

View File

@@ -60,9 +60,8 @@ export class HaIconInput extends LitElement {
static get styles() {
return css`
ha-icon {
position: absolute;
bottom: 2px;
right: 0;
position: relative;
bottom: 4px;
}
`;
}

View File

@@ -1,45 +0,0 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
} from "lit-element";
import { HomeAssistant } from "../../types";
import { ActionSelector } from "../../data/selector";
import { Action } from "../../data/script";
import "../../panels/config/automation/action/ha-automation-action";
@customElement("ha-selector-action")
export class HaActionSelector extends LitElement {
@property() public hass!: HomeAssistant;
@property() public selector!: ActionSelector;
@property() public value?: Action;
@property() public label?: string;
protected render() {
return html`<ha-automation-action
.actions=${this.value || []}
.hass=${this.hass}
></ha-automation-action>`;
}
static get styles(): CSSResult {
return css`
ha-automation-action {
display: block;
margin-bottom: 16px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-selector-action": HaActionSelector;
}
}

View File

@@ -1,16 +1,7 @@
import {
customElement,
html,
internalProperty,
LitElement,
property,
} from "lit-element";
import { customElement, html, LitElement, property } from "lit-element";
import { HomeAssistant } from "../../types";
import { AreaSelector } from "../../data/selector";
import "../ha-area-picker";
import { ConfigEntry, getConfigEntries } from "../../data/config_entries";
import { DeviceRegistryEntry } from "../../data/device_registry";
import { EntityRegistryEntry } from "../../data/entity_registry";
@customElement("ha-selector-area")
export class HaAreaSelector extends LitElement {
@@ -22,76 +13,14 @@ export class HaAreaSelector extends LitElement {
@property() public label?: string;
@internalProperty() public _configEntries?: ConfigEntry[];
protected updated(changedProperties) {
if (changedProperties.has("selector")) {
const oldSelector = changedProperties.get("selector");
if (
oldSelector !== this.selector &&
this.selector.area.device?.integration
) {
this._loadConfigEntries();
}
}
}
protected render() {
return html`<ha-area-picker
.hass=${this.hass}
.value=${this.value}
.label=${this.label}
no-add
.deviceFilter=${(device) => this._filterDevices(device)}
.entityFilter=${(entity) => this._filterEntities(entity)}
.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}
></ha-area-picker>`;
}
private _filterEntities(entity: EntityRegistryEntry): boolean {
if (this.selector.area.entity?.integration) {
if (entity.platform !== this.selector.area.entity.integration) {
return false;
}
}
return true;
}
private _filterDevices(device: DeviceRegistryEntry): boolean {
if (
this.selector.area.device?.manufacturer &&
device.manufacturer !== this.selector.area.device.manufacturer
) {
return false;
}
if (
this.selector.area.device?.model &&
device.model !== this.selector.area.device.model
) {
return false;
}
if (this.selector.area.device?.integration) {
if (
!this._configEntries?.some((entry) =>
device.config_entries.includes(entry.entry_id)
)
) {
return false;
}
}
return true;
}
private async _loadConfigEntries() {
this._configEntries = (await getConfigEntries(this.hass)).filter(
(entry) => entry.domain === this.selector.area.device?.integration
);
}
}
declare global {

View File

@@ -19,7 +19,7 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
@property() public selector!: EntitySelector;
@internalProperty() private _entityPlaformLookup?: Record<string, string>;
@internalProperty() private _entities?: Record<string, string>;
@property() public value?: any;
@@ -45,7 +45,7 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
}
entityLookup[confEnt.entity_id] = confEnt.platform;
}
this._entityPlaformLookup = entityLookup;
this._entities = entityLookup;
}),
];
}
@@ -66,9 +66,8 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
}
if (this.selector.entity.integration) {
if (
!this._entityPlaformLookup ||
this._entityPlaformLookup[entity.entity_id] !==
this.selector.entity.integration
!this._entities ||
this._entities[entity.entity_id] !== this.selector.entity.integration
) {
return false;
}

View File

@@ -1,153 +0,0 @@
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
} from "lit-element";
import { HomeAssistant } from "../../types";
import { TargetSelector } from "../../data/selector";
import { ConfigEntry, getConfigEntries } from "../../data/config_entries";
import { DeviceRegistryEntry } from "../../data/device_registry";
import "../ha-target-picker";
import "@material/mwc-list/mwc-list-item";
import "@polymer/paper-input/paper-input";
import "@material/mwc-list/mwc-list";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../../data/entity_registry";
import { Target } from "../../data/target";
import "@material/mwc-tab-bar/mwc-tab-bar";
import "@material/mwc-tab/mwc-tab";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
@customElement("ha-selector-target")
export class HaTargetSelector extends SubscribeMixin(LitElement) {
@property() public hass!: HomeAssistant;
@property() public selector!: TargetSelector;
@property() public value?: Target;
@property() public label?: string;
@internalProperty() private _entityPlaformLookup?: Record<string, string>;
@internalProperty() private _configEntries?: ConfigEntry[];
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection!, (entities) => {
const entityLookup = {};
for (const confEnt of entities) {
if (!confEnt.platform) {
continue;
}
entityLookup[confEnt.entity_id] = confEnt.platform;
}
this._entityPlaformLookup = entityLookup;
}),
];
}
protected updated(changedProperties) {
if (changedProperties.has("selector")) {
const oldSelector = changedProperties.get("selector");
if (
oldSelector !== this.selector &&
this.selector.target.device?.integration
) {
this._loadConfigEntries();
}
}
}
protected render() {
return html`<ha-target-picker
.hass=${this.hass}
.value=${this.value}
.deviceFilter=${(device) => this._filterDevices(device)}
.entityRegFilter=${(entity: EntityRegistryEntry) =>
this._filterRegEntities(entity)}
.entityFilter=${(entity: HassEntity) => this._filterEntities(entity)}
.includeDeviceClasses=${this.selector.target.entity?.device_class
? [this.selector.target.entity.device_class]
: undefined}
.includeDomains=${this.selector.target.entity?.domain
? [this.selector.target.entity.domain]
: undefined}
></ha-target-picker>`;
}
private _filterEntities(entity: HassEntity): boolean {
if (this.selector.target.entity?.integration) {
if (
!this._entityPlaformLookup ||
this._entityPlaformLookup[entity.entity_id] !==
this.selector.target.entity.integration
) {
return false;
}
}
return true;
}
private _filterRegEntities(entity: EntityRegistryEntry): boolean {
if (this.selector.target.entity?.integration) {
if (entity.platform !== this.selector.target.entity.integration) {
return false;
}
}
return true;
}
private _filterDevices(device: DeviceRegistryEntry): boolean {
if (
this.selector.target.device?.manufacturer &&
device.manufacturer !== this.selector.target.device.manufacturer
) {
return false;
}
if (
this.selector.target.device?.model &&
device.model !== this.selector.target.device.model
) {
return false;
}
if (this.selector.target.device?.integration) {
if (
!this._configEntries?.some((entry) =>
device.config_entries.includes(entry.entry_id)
)
) {
return false;
}
}
return true;
}
private async _loadConfigEntries() {
this._configEntries = (await getConfigEntries(this.hass)).filter(
(entry) => entry.domain === this.selector.target.device?.integration
);
}
static get styles(): CSSResult {
return css`
ha-target-picker {
margin: 0 -8px;
display: block;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-selector-target": HaTargetSelector;
}
}

View File

@@ -5,11 +5,9 @@ import { HomeAssistant } from "../../types";
import "./ha-selector-entity";
import "./ha-selector-device";
import "./ha-selector-area";
import "./ha-selector-target";
import "./ha-selector-number";
import "./ha-selector-boolean";
import "./ha-selector-time";
import "./ha-selector-action";
import { Selector } from "../../data/selector";
@customElement("ha-selector")

View File

@@ -1,4 +1,4 @@
import "@polymer/paper-item/paper-item-body";
import "@material/mwc-list/mwc-list-item";
import {
css,
CSSResult,
@@ -18,18 +18,10 @@ export class HaSettingsRow extends LitElement {
protected render(): SVGTemplateResult {
return html`
<style>
paper-item-body {
padding-right: 16px;
}
</style>
<paper-item-body
?two-line=${!this.threeLine}
?three-line=${this.threeLine}
>
<mwc-list-item noninteractive ?twoline=${!this.threeLine}>
<slot name="heading"></slot>
<div secondary><slot name="description"></slot></div>
</paper-item-body>
<span slot="secondary"><slot name="description"></slot></span>
</mwc-list-item>
<slot></slot>
`;
}
@@ -42,6 +34,7 @@ export class HaSettingsRow extends LitElement {
align-content: normal;
align-self: auto;
align-items: center;
justify-content: space-between;
}
:host([narrow]) {
align-items: normal;
@@ -49,6 +42,9 @@ export class HaSettingsRow extends LitElement {
border-top: 1px solid var(--divider-color);
padding-bottom: 8px;
}
mwc-list-item {
--mdc-list-side-padding: 0;
}
::slotted(ha-switch) {
padding: 16px 0;
}

View File

@@ -1,595 +0,0 @@
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
query,
unsafeCSS,
} from "lit-element";
import { HomeAssistant } from "../types";
// @ts-ignore
import chipStyles from "@material/chips/dist/mdc.chips.min.css";
import {
mdiSofa,
mdiDevices,
mdiClose,
mdiPlus,
mdiUnfoldMoreVertical,
} from "@mdi/js";
import "./ha-svg-icon";
import "./ha-icon";
import "@material/mwc-icon-button/mwc-icon-button";
import { classMap } from "lit-html/directives/class-map";
import "@material/mwc-button/mwc-button";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
AreaRegistryEntry,
subscribeAreaRegistry,
} from "../data/area_registry";
import {
DeviceRegistryEntry,
subscribeDeviceRegistry,
} from "../data/device_registry";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../data/entity_registry";
import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { computeStateName } from "../common/entity/compute_state_name";
import { stateIcon } from "../common/entity/state_icon";
import { fireEvent } from "../common/dom/fire_event";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
import { computeDomain } from "../common/entity/compute_domain";
import { Target } from "../data/target";
import { ensureArray } from "../common/ensure-array";
import "./entity/ha-entity-picker";
import "./device/ha-device-picker";
import "./ha-area-picker";
import type { HaEntityPickerEntityFilterFunc } from "./entity/ha-entity-picker";
import "@polymer/paper-tooltip/paper-tooltip";
@customElement("ha-target-picker")
export class HaTargetPicker extends SubscribeMixin(LitElement) {
@property() public hass!: HomeAssistant;
@property() public value?: Target;
@property() public label?: string;
/**
* Show only targets with entities from specific domains.
* @type {Array}
* @attr include-domains
*/
@property({ type: Array, attribute: "include-domains" })
public includeDomains?: string[];
/**
* Show only targets 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 entityRegFilter?: (entity: EntityRegistryEntry) => boolean;
@property() public entityFilter?: HaEntityPickerEntityFilterFunc;
@internalProperty() private _areas?: { [areaId: string]: AreaRegistryEntry };
@internalProperty() private _devices?: {
[deviceId: string]: DeviceRegistryEntry;
};
@internalProperty() private _entities?: EntityRegistryEntry[];
@internalProperty() private _addMode?: "area_id" | "entity_id" | "device_id";
@query("#input") private _inputElement?;
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeAreaRegistry(this.hass.connection!, (areas) => {
const areaLookup: { [areaId: string]: AreaRegistryEntry } = {};
for (const area of areas) {
areaLookup[area.area_id] = area;
}
this._areas = areaLookup;
}),
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
const deviceLookup: { [deviceId: string]: DeviceRegistryEntry } = {};
for (const device of devices) {
deviceLookup[device.id] = device;
}
this._devices = deviceLookup;
}),
subscribeEntityRegistry(this.hass.connection!, (entities) => {
this._entities = entities;
}),
];
}
protected render() {
if (!this._areas || !this._devices || !this._entities) {
return html``;
}
return html`<div class="mdc-chip-set items">
${ensureArray(this.value?.area_id)?.map((area_id) => {
const area = this._areas![area_id];
return this._renderChip(
"area_id",
area_id,
area?.name || area_id,
undefined,
mdiSofa
);
})}
${ensureArray(this.value?.device_id)?.map((device_id) => {
const device = this._devices![device_id];
return this._renderChip(
"device_id",
device_id,
device?.name || device_id,
undefined,
mdiDevices
);
})}
${ensureArray(this.value?.entity_id)?.map((entity_id) => {
const entity = this.hass.states[entity_id];
return this._renderChip(
"entity_id",
entity_id,
entity ? computeStateName(entity) : entity_id,
entity ? stateIcon(entity) : undefined
);
})}
</div>
${this._renderPicker()}
<div class="mdc-chip-set">
<div
class="mdc-chip area_id add"
.type=${"area_id"}
@click=${this._showPicker}
>
<div class="mdc-chip__ripple"></div>
<ha-svg-icon
class="mdc-chip__icon mdc-chip__icon--leading"
.path=${mdiPlus}
></ha-svg-icon>
<span role="gridcell">
<span role="button" tabindex="0" class="mdc-chip__primary-action">
<span class="mdc-chip__text"
>${this.hass.localize(
"ui.components.target-picker.add_area_id"
)}</span
>
</span>
</span>
</div>
<div
class="mdc-chip device_id add"
.type=${"device_id"}
@click=${this._showPicker}
>
<div class="mdc-chip__ripple"></div>
<ha-svg-icon
class="mdc-chip__icon mdc-chip__icon--leading"
.path=${mdiPlus}
></ha-svg-icon>
<span role="gridcell">
<span role="button" tabindex="0" class="mdc-chip__primary-action">
<span class="mdc-chip__text"
>${this.hass.localize(
"ui.components.target-picker.add_device_id"
)}</span
>
</span>
</span>
</div>
<div
class="mdc-chip entity_id add"
.type=${"entity_id"}
@click=${this._showPicker}
>
<div class="mdc-chip__ripple"></div>
<ha-svg-icon
class="mdc-chip__icon mdc-chip__icon--leading"
.path=${mdiPlus}
></ha-svg-icon>
<span role="gridcell">
<span role="button" tabindex="0" class="mdc-chip__primary-action">
<span class="mdc-chip__text"
>${this.hass.localize(
"ui.components.target-picker.add_entity_id"
)}</span
>
</span>
</span>
</div>
</div>`;
}
private async _showPicker(ev) {
this._addMode = ev.currentTarget.type;
await this.updateComplete;
setTimeout(() => {
this._inputElement?.open();
this._inputElement?.focus();
}, 0);
}
private _renderChip(
type: string,
id: string,
name: string,
icon?: string,
iconPath?: string
) {
return html`
<div
class="mdc-chip ${classMap({
[type]: true,
})}"
>
${iconPath
? html`<ha-svg-icon
class="mdc-chip__icon mdc-chip__icon--leading"
.path=${iconPath}
></ha-svg-icon>`
: ""}
${icon
? html`<ha-icon
class="mdc-chip__icon mdc-chip__icon--leading"
.icon=${icon}
></ha-icon>`
: ""}
<span role="gridcell">
<span role="button" tabindex="0" class="mdc-chip__primary-action">
<span class="mdc-chip__text">${name}</span>
</span>
</span>
${type === "entity_id"
? ""
: html` <span role="gridcell">
<mwc-icon-button
class="expand-btn mdc-chip__icon mdc-chip__icon--trailing"
tabindex="-1"
role="button"
.label=${"Expand"}
.id=${id}
.type=${type}
@click=${this._handleExpand}
>
<ha-svg-icon .path=${mdiUnfoldMoreVertical}></ha-svg-icon>
</mwc-icon-button>
<paper-tooltip class="expand" animation-delay="0"
>${this.hass.localize(
`ui.components.target-picker.expand_${type}`
)}</paper-tooltip
>
</span>`}
<span role="gridcell">
<mwc-icon-button
class="mdc-chip__icon mdc-chip__icon--trailing"
tabindex="-1"
role="button"
.label=${"Remove"}
.id=${id}
.type=${type}
@click=${this._handleRemove}
>
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button>
<paper-tooltip animation-delay="0"
>${this.hass.localize(
`ui.components.target-picker.remove_${type}`
)}</paper-tooltip
>
</span>
</div>
`;
}
private _renderPicker() {
switch (this._addMode) {
case "area_id":
return html`<ha-area-picker
.hass=${this.hass}
id="input"
.type=${"area_id"}
.label=${this.hass.localize(
"ui.components.target-picker.add_area_id"
)}
no-add
.deviceFilter=${this.deviceFilter}
.entityFilter=${this.entityRegFilter}
.includeDeviceClasses=${this.includeDeviceClasses}
.includeDomains=${this.includeDomains}
@value-changed=${this._targetPicked}
></ha-area-picker>`;
case "device_id":
return html`<ha-device-picker
.hass=${this.hass}
id="input"
.type=${"device_id"}
.label=${this.hass.localize(
"ui.components.target-picker.add_device_id"
)}
.deviceFilter=${this.deviceFilter}
.entityFilter=${this.entityRegFilter}
.includeDeviceClasses=${this.includeDeviceClasses}
.includeDomains=${this.includeDomains}
@value-changed=${this._targetPicked}
></ha-device-picker>`;
case "entity_id":
return html`<ha-entity-picker
.hass=${this.hass}
id="input"
.type=${"entity_id"}
.label=${this.hass.localize(
"ui.components.target-picker.add_entity_id"
)}
.entityFilter=${this.entityFilter}
.includeDeviceClasses=${this.includeDeviceClasses}
.includeDomains=${this.includeDomains}
@value-changed=${this._targetPicked}
></ha-entity-picker>`;
}
return html``;
}
private _targetPicked(ev) {
ev.stopPropagation();
if (!ev.detail.value) {
return;
}
const value = ev.detail.value;
const target = ev.currentTarget;
target.value = "";
this._addMode = undefined;
fireEvent(this, "value-changed", {
value: this.value
? {
...this.value,
[target.type]: this.value[target.type]
? [...ensureArray(this.value[target.type]), value]
: value,
}
: { [target.type]: value },
});
}
private _handleExpand(ev) {
const target = ev.currentTarget as any;
const newDevices: string[] = [];
const newEntities: string[] = [];
if (target.type === "area_id") {
Object.values(this._devices!).forEach((device) => {
if (
device.area_id === target.id &&
!this.value!.device_id?.includes(device.id) &&
this._deviceMeetsFilter(device)
) {
newDevices.push(device.id);
}
});
this._entities!.forEach((entity) => {
if (
entity.area_id === target.id &&
!this.value!.entity_id?.includes(entity.entity_id) &&
this._entityRegMeetsFilter(entity)
) {
newEntities.push(entity.entity_id);
}
});
} else if (target.type === "device_id") {
this._entities!.forEach((entity) => {
if (
entity.device_id === target.id &&
!this.value!.entity_id?.includes(entity.entity_id) &&
this._entityRegMeetsFilter(entity)
) {
newEntities.push(entity.entity_id);
}
});
} else {
return;
}
let value = this.value;
if (newEntities.length) {
value = this._addItems(value, "entity_id", newEntities);
}
if (newDevices.length) {
value = this._addItems(value, "device_id", newDevices);
}
value = this._removeItem(value, target.type, target.id);
fireEvent(this, "value-changed", { value });
}
private _handleRemove(ev) {
const target = ev.currentTarget as any;
fireEvent(this, "value-changed", {
value: this._removeItem(this.value, target.type, target.id),
});
}
private _addItems(
value: this["value"],
type: string,
ids: string[]
): this["value"] {
return {
...value,
[type]: value![type] ? ensureArray(value![type])!.concat(ids) : ids,
};
}
private _removeItem(
value: this["value"],
type: string,
id: string
): this["value"] {
return {
...value,
[type]: ensureArray(value![type])!.filter((val) => val !== id),
};
}
private _deviceMeetsFilter(device: DeviceRegistryEntry): boolean {
const devEntities = this._entities?.filter(
(entity) => entity.device_id === device.id
);
if (this.includeDomains) {
if (!devEntities || !devEntities.length) {
return false;
}
if (
!devEntities.some((entity) =>
this.includeDomains!.includes(computeDomain(entity.entity_id))
)
) {
return false;
}
}
if (this.includeDeviceClasses) {
if (!devEntities || !devEntities.length) {
return false;
}
if (
!devEntities.some((entity) => {
const stateObj = this.hass.states[entity.entity_id];
if (!stateObj) {
return false;
}
return (
stateObj.attributes.device_class &&
this.includeDeviceClasses!.includes(
stateObj.attributes.device_class
)
);
})
) {
return false;
}
}
if (this.deviceFilter) {
return this.deviceFilter(device);
}
return true;
}
private _entityRegMeetsFilter(entity: EntityRegistryEntry): boolean {
if (
this.includeDomains &&
!this.includeDomains.includes(computeDomain(entity.entity_id))
) {
return false;
}
if (this.includeDeviceClasses) {
const stateObj = this.hass.states[entity.entity_id];
if (!stateObj) {
return false;
}
if (
!stateObj.attributes.device_class ||
!this.includeDeviceClasses!.includes(stateObj.attributes.device_class)
) {
return false;
}
}
if (this.entityRegFilter) {
return this.entityRegFilter(entity);
}
return true;
}
static get styles(): CSSResult {
return css`
${unsafeCSS(chipStyles)}
.mdc-chip {
color: var(--primary-text-color);
}
.items {
z-index: 2;
}
.mdc-chip.add {
color: rgba(0, 0, 0, 0.87);
}
.mdc-chip:not(.add) {
cursor: default;
}
.mdc-chip mwc-icon-button {
--mdc-icon-button-size: 24px;
display: flex;
align-items: center;
outline: none;
}
.mdc-chip mwc-icon-button ha-svg-icon {
border-radius: 50%;
background: var(--secondary-text-color);
}
.mdc-chip__icon.mdc-chip__icon--trailing {
width: 16px;
height: 16px;
--mdc-icon-size: 14px;
color: var(--card-background-color);
}
.mdc-chip__icon--leading {
display: flex;
align-items: center;
justify-content: center;
--mdc-icon-size: 20px;
border-radius: 50%;
padding: 6px;
margin-left: -14px !important;
}
.expand-btn {
margin-right: 0;
}
.mdc-chip.area_id:not(.add) {
border: 2px solid #fed6a4;
background: var(--card-background-color);
}
.mdc-chip.area_id:not(.add) .mdc-chip__icon--leading,
.mdc-chip.area_id.add {
background: #fed6a4;
}
.mdc-chip.device_id:not(.add) {
border: 2px solid #a8e1fb;
background: var(--card-background-color);
}
.mdc-chip.device_id:not(.add) .mdc-chip__icon--leading,
.mdc-chip.device_id.add {
background: #a8e1fb;
}
.mdc-chip.entity_id:not(.add) {
border: 2px solid #d2e7b9;
background: var(--card-background-color);
}
.mdc-chip.entity_id:not(.add) .mdc-chip__icon--leading,
.mdc-chip.entity_id.add {
background: #d2e7b9;
}
.mdc-chip:hover {
z-index: 5;
}
paper-tooltip.expand {
min-width: 200px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-target-picker": HaTargetPicker;
}
}

View File

@@ -6,7 +6,7 @@ import { navigate } from "../common/navigate";
import { Context, HomeAssistant } from "../types";
import { BlueprintInput } from "./blueprint";
import { DeviceCondition, DeviceTrigger } from "./device_automation";
import { Action, MODES } from "./script";
import { Action } from "./script";
export interface AutomationEntity extends HassEntityBase {
attributes: HassEntityAttributeBase & {
@@ -26,7 +26,7 @@ export interface ManualAutomationConfig {
trigger: Trigger[];
condition?: Condition[];
action: Action[];
mode?: typeof MODES[number];
mode?: "single" | "restart" | "queued" | "parallel";
max?: number;
}

View File

@@ -17,7 +17,6 @@ export interface DeviceRegistryEntry {
area_id?: string;
name_by_user?: string;
entry_type: "service" | null;
disabled_by: string | null;
}
export interface DeviceEntityLookup {
@@ -27,7 +26,6 @@ export interface DeviceEntityLookup {
export interface DeviceRegistryEntryMutableParams {
area_id?: string | null;
name_by_user?: string | null;
disabled_by?: string | null;
}
export const fallbackDeviceName = (

View File

@@ -7,13 +7,13 @@ import { navigate } from "../common/navigate";
import { HomeAssistant } from "../types";
import { Condition, Trigger } from "./automation";
export const MODES = ["single", "restart", "queued", "parallel"] as const;
export const MODES = ["single", "restart", "queued", "parallel"];
export const MODES_MAX = ["queued", "parallel"];
export interface ScriptEntity extends HassEntityBase {
attributes: HassEntityAttributeBase & {
last_triggered: string;
mode: typeof MODES[number];
mode: "single" | "restart" | "queued" | "parallel";
current?: number;
max?: number;
};
@@ -23,7 +23,7 @@ export interface ScriptConfig {
alias: string;
sequence: Action[];
icon?: string;
mode?: typeof MODES[number];
mode?: "single" | "restart" | "queued" | "parallel";
max?: number;
}

View File

@@ -2,11 +2,9 @@ export type Selector =
| EntitySelector
| DeviceSelector
| AreaSelector
| TargetSelector
| NumberSelector
| BooleanSelector
| TimeSelector
| ActionSelector;
| TimeSelector;
export interface EntitySelector {
entity: {
@@ -21,41 +19,13 @@ export interface DeviceSelector {
integration?: string;
manufacturer?: string;
model?: string;
entity?: {
domain?: EntitySelector["entity"]["domain"];
device_class?: EntitySelector["entity"]["device_class"];
};
entity?: EntitySelector["entity"];
};
}
export interface AreaSelector {
area: {
entity?: {
integration?: EntitySelector["entity"]["integration"];
domain?: EntitySelector["entity"]["domain"];
device_class?: EntitySelector["entity"]["device_class"];
};
device?: {
integration?: DeviceSelector["device"]["integration"];
manufacturer?: DeviceSelector["device"]["manufacturer"];
model?: DeviceSelector["device"]["model"];
};
};
}
export interface TargetSelector {
target: {
entity?: {
integration?: EntitySelector["entity"]["integration"];
domain?: EntitySelector["entity"]["domain"];
device_class?: EntitySelector["entity"]["device_class"];
};
device?: {
integration?: DeviceSelector["device"]["integration"];
manufacturer?: DeviceSelector["device"]["manufacturer"];
model?: DeviceSelector["device"]["model"];
};
};
// eslint-disable-next-line @typescript-eslint/ban-types
area: {};
}
export interface NumberSelector {
@@ -77,8 +47,3 @@ export interface TimeSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
time: {};
}
export interface ActionSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
action: {};
}

View File

@@ -1,5 +0,0 @@
export interface Target {
entity_id?: string[];
device_id?: string[];
area_id?: string[];
}

View File

@@ -20,7 +20,6 @@ export interface User {
export interface UpdateUserParams {
name?: User["name"];
is_active?: User["is_active"];
group_ids?: User["group_ids"];
}

View File

@@ -39,7 +39,7 @@ export class HaWaitForTriggerAction extends LitElement
)}
>
<ha-switch
.checked=${continue_on_timeout ?? true}
.checked=${continue_on_timeout}
@change=${this._continueChanged}
></ha-switch>
</ha-formfield>

View File

@@ -63,7 +63,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
protected render() {
const blueprint = this._blueprint;
return html`<ha-config-section vertical .isWide=${this.isWide}>
return html`<ha-config-section .isWide=${this.isWide}>
${!this.narrow
? html` <span slot="header">${this.config.alias}</span> `
: ""}
@@ -119,7 +119,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
</ha-card>
</ha-config-section>
<ha-config-section vertical .isWide=${this.isWide}>
<ha-config-section .isWide=${this.isWide}>
<span slot="header"
>${this.hass.localize(
"ui.panel.config.automation.editor.blueprint.header"
@@ -185,7 +185,6 @@ export class HaBlueprintAutomationEditor extends LitElement {
></ha-selector>`
: html`<paper-input
.key=${key}
required
.value=${this.config.use_blueprint.input &&
this.config.use_blueprint.input[key]}
@value-changed=${this._inputChanged}
@@ -276,6 +275,9 @@ export class HaBlueprintAutomationEditor extends LitElement {
return [
haStyle,
css`
ha-card {
overflow: hidden;
}
.padding {
padding: 16px;
}
@@ -302,10 +304,10 @@ export class HaBlueprintAutomationEditor extends LitElement {
border-top: 1px solid var(--divider-color);
}
:host(:not([narrow])) ha-settings-row paper-input {
width: 60%;
width: 50%;
}
:host(:not([narrow])) ha-settings-row ha-selector {
width: 60%;
width: 50%;
}
`,
];

View File

@@ -206,7 +206,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
? html`<blueprint-automation-editor
.hass=${this.hass}
.narrow=${this.narrow}
.isWide=${this.isWide}
.stateObj=${stateObj}
.config=${this._config}
@value-changed=${this._valueChanged}
@@ -214,7 +213,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
: html`<manual-automation-editor
.hass=${this.hass}
.narrow=${this.narrow}
.isWide=${this.isWide}
.stateObj=${stateObj}
.config=${this._config}
@value-changed=${this._valueChanged}

View File

@@ -8,6 +8,7 @@ import {
html,
LitElement,
property,
internalProperty,
PropertyValues,
TemplateResult,
} from "lit-element";
@@ -30,7 +31,7 @@ export class HaDeviceEntitiesCard extends LitElement {
@property() public entities!: EntityRegistryStateEntry[];
@property() public showDisabled = false;
@internalProperty() private _showDisabled = false;
private _entityRows: Array<LovelaceRow | HuiErrorCard> = [];
@@ -67,7 +68,7 @@ export class HaDeviceEntitiesCard extends LitElement {
})}
</div>
${disabledEntities.length
? !this.showDisabled
? !this._showDisabled
? html`
<button
class="show-more"
@@ -118,7 +119,7 @@ export class HaDeviceEntitiesCard extends LitElement {
}
private _toggleShowDisabled() {
this.showDisabled = !this.showDisabled;
this._showDisabled = !this._showDisabled;
}
private _renderEntity(entry: EntityRegistryStateEntry): TemplateResult {
@@ -226,9 +227,3 @@ export class HaDeviceEntitiesCard extends LitElement {
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-device-entities-card": HaDeviceEntitiesCard;
}
}

View File

@@ -19,11 +19,10 @@ import {
import { DeviceRegistryDetailDialogParams } from "./show-dialog-device-registry-detail";
import { HomeAssistant } from "../../../../types";
import type { HaSwitch } from "../../../../components/ha-switch";
import { PolymerChangedEvent } from "../../../../polymer-types";
import { computeDeviceName } from "../../../../data/device_registry";
import { fireEvent } from "../../../../common/dom/fire_event";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
import { haStyleDialog } from "../../../../resources/styles";
@customElement("dialog-device-registry-detail")
class DialogDeviceRegistryDetail extends LitElement {
@@ -37,8 +36,6 @@ class DialogDeviceRegistryDetail extends LitElement {
@internalProperty() private _areaId?: string;
@internalProperty() private _disabledBy!: string | null;
@internalProperty() private _submitting?: boolean;
public async showDialog(
@@ -48,7 +45,6 @@ class DialogDeviceRegistryDetail extends LitElement {
this._error = undefined;
this._nameByUser = this._params.device.name_by_user || "";
this._areaId = this._params.device.area_id;
this._disabledBy = this._params.device.disabled_by;
await this.updateComplete;
}
@@ -84,32 +80,6 @@ class DialogDeviceRegistryDetail extends LitElement {
.value=${this._areaId}
@value-changed=${this._areaPicked}
></ha-area-picker>
<div class="row">
<ha-switch
.checked=${!this._disabledBy}
@change=${this._disabledByChanged}
>
</ha-switch>
<div>
<div>
${this.hass.localize("ui.panel.config.devices.enabled_label")}
</div>
<div class="secondary">
${this._disabledBy && this._disabledBy !== "user"
? this.hass.localize(
"ui.panel.config.devices.enabled_cause",
"cause",
this.hass.localize(
`config_entry.disabled_by.${this._disabledBy}`
)
)
: ""}
${this.hass.localize(
"ui.panel.config.devices.enabled_description"
)}
</div>
</div>
</div>
</div>
</div>
<mwc-button
@@ -139,17 +109,12 @@ class DialogDeviceRegistryDetail extends LitElement {
this._areaId = event.detail.value;
}
private _disabledByChanged(ev: Event): void {
this._disabledBy = (ev.target as HaSwitch).checked ? null : "user";
}
private async _updateEntry(): Promise<void> {
this._submitting = true;
try {
await this._params!.updateEntry({
name_by_user: this._nameByUser.trim() || null,
area_id: this._areaId || null,
disabled_by: this._disabledBy || null,
});
this._params = undefined;
} catch (err) {
@@ -163,7 +128,6 @@ class DialogDeviceRegistryDetail extends LitElement {
static get styles(): CSSResult[] {
return [
haStyle,
haStyleDialog,
css`
.form {
@@ -175,15 +139,6 @@ class DialogDeviceRegistryDetail extends LitElement {
.error {
color: var(--error-color);
}
ha-switch {
margin-right: 16px;
}
.row {
margin-top: 8px;
color: var(--primary-text-color);
display: flex;
align-items: center;
}
`,
];
}

View File

@@ -46,7 +46,6 @@ import "./device-detail/ha-device-entities-card";
import "./device-detail/ha-device-info-card";
import { showDeviceAutomationDialog } from "./device-detail/show-dialog-device-automation";
import { brandsUrl } from "../../../util/brands-url";
import { haStyle } from "../../../resources/styles";
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
stateName?: string | null;
@@ -247,28 +246,6 @@ export class HaConfigDevicePage extends LitElement {
.devices=${this.devices}
.device=${device}
>
${
device.disabled_by
? html`
<div>
<p class="warning">
${this.hass.localize(
"ui.panel.config.devices.enabled_cause",
"cause",
this.hass.localize(
`ui.panel.config.devices.disabled_by.${device.disabled_by}`
)
)}
</p>
</div>
<div class="card-actions" slot="actions">
<mwc-button unelevated @click=${this._enableDevice}>
${this.hass.localize("ui.common.enable")}
</mwc-button>
</div>
`
: html``
}
${this._renderIntegrationInfo(device, integrations)}
</ha-device-info-card>
@@ -278,7 +255,6 @@ export class HaConfigDevicePage extends LitElement {
<ha-device-entities-card
.hass=${this.hass}
.entities=${entities}
.showDisabled=${device.disabled_by !== null}
>
</ha-device-entities-card>
`
@@ -296,14 +272,9 @@ export class HaConfigDevicePage extends LitElement {
)}
<ha-icon-button
@click=${this._showAutomationDialog}
.disabled=${device.disabled_by}
title=${device.disabled_by
? this.hass.localize(
"ui.panel.config.devices.automation.create_disabled"
)
: this.hass.localize(
"ui.panel.config.devices.automation.create"
)}
title=${this.hass.localize(
"ui.panel.config.devices.automation.create"
)}
icon="hass:plus-circle"
></ha-icon-button>
</h1>
@@ -371,16 +342,9 @@ export class HaConfigDevicePage extends LitElement {
<ha-icon-button
@click=${this._createScene}
.disabled=${device.disabled_by}
title=${
device.disabled_by
? this.hass.localize(
"ui.panel.config.devices.scene.create_disabled"
)
: this.hass.localize(
"ui.panel.config.devices.scene.create"
)
}
title=${this.hass.localize(
"ui.panel.config.devices.scene.create"
)}
icon="hass:plus-circle"
></ha-icon-button>
</h1>
@@ -451,14 +415,9 @@ export class HaConfigDevicePage extends LitElement {
)}
<ha-icon-button
@click=${this._showScriptDialog}
.disabled=${device.disabled_by}
title=${device.disabled_by
? this.hass.localize(
"ui.panel.config.devices.script.create_disabled"
)
: this.hass.localize(
"ui.panel.config.devices.script.create"
)}
title=${this.hass.localize(
"ui.panel.config.devices.script.create"
)}
icon="hass:plus-circle"
></ha-icon-button>
</h1>
@@ -673,137 +632,128 @@ export class HaConfigDevicePage extends LitElement {
});
}
private async _enableDevice(): Promise<void> {
await updateDeviceRegistryEntry(this.hass, this.deviceId, {
disabled_by: null,
});
}
static get styles(): CSSResult {
return css`
.container {
display: flex;
flex-wrap: wrap;
margin: auto;
max-width: 1000px;
margin-top: 32px;
margin-bottom: 32px;
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
.container {
display: flex;
flex-wrap: wrap;
margin: auto;
max-width: 1000px;
margin-top: 32px;
margin-bottom: 32px;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-header ha-icon-button {
margin-right: -8px;
color: var(--primary-color);
height: auto;
}
.card-header ha-icon-button {
margin-right: -8px;
color: var(--primary-color);
height: auto;
}
.device-info {
padding: 16px;
}
.device-info {
padding: 16px;
}
.show-more {
}
.show-more {
}
h1 {
margin: 0;
font-family: var(--paper-font-headline_-_font-family);
-webkit-font-smoothing: var(
--paper-font-headline_-_-webkit-font-smoothing
);
font-size: var(--paper-font-headline_-_font-size);
font-weight: var(--paper-font-headline_-_font-weight);
letter-spacing: var(--paper-font-headline_-_letter-spacing);
line-height: var(--paper-font-headline_-_line-height);
opacity: var(--dark-primary-opacity);
}
h1 {
margin: 0;
font-family: var(--paper-font-headline_-_font-family);
-webkit-font-smoothing: var(
--paper-font-headline_-_-webkit-font-smoothing
);
font-size: var(--paper-font-headline_-_font-size);
font-weight: var(--paper-font-headline_-_font-weight);
letter-spacing: var(--paper-font-headline_-_letter-spacing);
line-height: var(--paper-font-headline_-_line-height);
opacity: var(--dark-primary-opacity);
}
.header {
display: flex;
justify-content: space-between;
}
.header {
display: flex;
justify-content: space-between;
}
.column,
.fullwidth {
padding: 8px;
box-sizing: border-box;
}
.column {
width: 33%;
flex-grow: 1;
}
.fullwidth {
width: 100%;
flex-grow: 1;
}
.column,
.fullwidth {
padding: 8px;
box-sizing: border-box;
}
.column {
width: 33%;
flex-grow: 1;
}
.fullwidth {
width: 100%;
flex-grow: 1;
}
.header-right {
align-self: center;
}
.header-right {
align-self: center;
}
.header-right img {
height: 30px;
}
.header-right img {
height: 30px;
}
.header-right {
display: flex;
}
.header-right {
display: flex;
}
.header-right:first-child {
width: 100%;
justify-content: flex-end;
}
.header-right:first-child {
width: 100%;
justify-content: flex-end;
}
.header-right > *:not(:first-child) {
margin-left: 16px;
}
.header-right > *:not(:first-child) {
margin-left: 16px;
}
.battery {
align-self: center;
align-items: center;
display: flex;
}
.battery {
align-self: center;
align-items: center;
display: flex;
}
.column > *:not(:first-child) {
margin-top: 16px;
}
.column > *:not(:first-child) {
margin-top: 16px;
}
:host([narrow]) .column {
width: 100%;
}
:host([narrow]) .column {
width: 100%;
}
:host([narrow]) .container {
margin-top: 0;
}
:host([narrow]) .container {
margin-top: 0;
}
paper-item {
cursor: pointer;
font-size: var(--paper-font-body1_-_font-size);
}
paper-item {
cursor: pointer;
font-size: var(--paper-font-body1_-_font-size);
}
paper-item.no-link {
cursor: default;
}
paper-item.no-link {
cursor: default;
}
a {
text-decoration: none;
color: var(--primary-color);
}
a {
text-decoration: none;
color: var(--primary-color);
}
ha-card {
padding-bottom: 8px;
}
ha-card {
padding-bottom: 8px;
}
ha-card a {
color: var(--primary-text-color);
}
`,
];
ha-card a {
color: var(--primary-text-color);
}
`;
}
}

View File

@@ -1,9 +1,5 @@
import { mdiPlus, mdiFilterVariant, mdiCancel } from "@mdi/js";
import "@material/mwc-list/mwc-list-item";
import "@polymer/paper-tooltip/paper-tooltip";
import { mdiPlus } from "@mdi/js";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
@@ -11,9 +7,7 @@ import {
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import memoizeOne from "memoize-one";
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
import { HASSDomEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate";
import { LocalizeFunc } from "../../../common/translations/localize";
@@ -24,7 +18,6 @@ import {
RowClickedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/entity/ha-battery-icon";
import "../../../components/ha-button-menu";
import { AreaRegistryEntry } from "../../../data/area_registry";
import { ConfigEntry } from "../../../data/config_entries";
import {
@@ -41,7 +34,6 @@ import { domainToName } from "../../../data/integration";
import "../../../layouts/hass-tabs-subpage-data-table";
import { HomeAssistant, Route } from "../../../types";
import { configSections } from "../ha-panel-config";
import { haStyle } from "../../../resources/styles";
interface DeviceRowData extends DeviceRegistryEntry {
device?: DeviceRowData;
@@ -72,12 +64,6 @@ export class HaConfigDeviceDashboard extends LitElement {
window.location.search
);
@internalProperty() private _showDisabled = false;
@internalProperty() private _filter = "";
@internalProperty() private _numHiddenDevices = 0;
private _activeFilters = memoizeOne(
(
entries: ConfigEntry[],
@@ -88,10 +74,6 @@ export class HaConfigDeviceDashboard extends LitElement {
filters.forEach((value, key) => {
switch (key) {
case "config_entry": {
// If we are requested to show the devices for a given config entry,
// also show the disabled ones by default.
this._showDisabled = true;
const configEntry = entries.find(
(entry) => entry.entry_id === value
);
@@ -123,7 +105,6 @@ export class HaConfigDeviceDashboard extends LitElement {
entities: EntityRegistryEntry[],
areas: AreaRegistryEntry[],
filters: URLSearchParams,
showDisabled: boolean,
localize: LocalizeFunc
) => {
// Some older installations might have devices pointing at invalid entryIDs
@@ -136,9 +117,6 @@ export class HaConfigDeviceDashboard extends LitElement {
deviceLookup[device.id] = device;
}
// If nothing gets filtered, this is our correct count of devices
let startLength = outputDevices.length;
const deviceEntityLookup: DeviceEntityLookup = {};
for (const entity of entities) {
if (!entity.device_id) {
@@ -167,7 +145,6 @@ export class HaConfigDeviceDashboard extends LitElement {
outputDevices = outputDevices.filter((device) =>
device.config_entries.includes(value)
);
startLength = outputDevices.length;
const configEntry = entries.find((entry) => entry.entry_id === value);
if (configEntry) {
filterDomains.push(configEntry.domain);
@@ -175,10 +152,6 @@ export class HaConfigDeviceDashboard extends LitElement {
}
});
if (!showDisabled) {
outputDevices = outputDevices.filter((device) => !device.disabled_by);
}
outputDevices = outputDevices.map((device) => {
return {
...device,
@@ -209,19 +182,16 @@ export class HaConfigDeviceDashboard extends LitElement {
};
});
this._numHiddenDevices = startLength - outputDevices.length;
return { devicesOutput: outputDevices, filteredDomains: filterDomains };
}
);
private _columns = memoizeOne(
(narrow: boolean, showDisabled: boolean): DataTableColumnContainer => {
(narrow: boolean): DataTableColumnContainer => {
const columns: DataTableColumnContainer = narrow
? {
name: {
title: this.hass.localize(
"ui.panel.config.devices.data_table.device"
),
title: "Device",
sortable: true,
filterable: true,
direction: "asc",
@@ -307,24 +277,6 @@ export class HaConfigDeviceDashboard extends LitElement {
: html` - `;
},
};
if (showDisabled) {
columns.disabled_by = {
title: "",
type: "icon",
template: (disabled_by) =>
disabled_by
? html`<div
tabindex="0"
style="display:inline-block; position: relative;"
>
<ha-svg-icon .path=${mdiCancel}></ha-svg-icon>
<paper-tooltip animation-delay="0" position="left">
${this.hass.localize("ui.panel.config.devices.disabled")}
</paper-tooltip>
</div>`
: "",
};
}
return columns;
}
);
@@ -346,119 +298,9 @@ export class HaConfigDeviceDashboard extends LitElement {
this.entities,
this.areas,
this._searchParms,
this._showDisabled,
this.hass.localize
);
const includeZHAFab = filteredDomains.includes("zha");
const activeFilters = this._activeFilters(
this.entries,
this._searchParms,
this.hass.localize
);
const headerToolbar = html`
<search-input
no-label-float
no-underline
@value-changed=${this._handleSearchChange}
.filter=${this._filter}
.label=${this.hass.localize("ui.panel.config.devices.picker.search")}
></search-input
>${activeFilters
? html`<div class="active-filters">
${this.narrow
? html` <div>
<ha-icon icon="hass:filter-variant"></ha-icon>
<paper-tooltip animation-delay="0" position="left">
${this.hass.localize(
"ui.panel.config.filtering.filtering_by"
)}
${activeFilters.join(", ")}
${this._numHiddenDevices
? "(" +
this.hass.localize(
"ui.panel.config.devices.picker.filter.hidden_devices",
"number",
this._numHiddenDevices
) +
")"
: ""}
</paper-tooltip>
</div>`
: `${this.hass.localize(
"ui.panel.config.filtering.filtering_by"
)} ${activeFilters.join(", ")}
${
this._numHiddenDevices
? "(" +
this.hass.localize(
"ui.panel.config.devices.picker.filter.hidden_devices",
"number",
this._numHiddenDevices
) +
")"
: ""
}
`}
<mwc-button @click=${this._clearFilter}
>${this.hass.localize(
"ui.panel.config.filtering.clear"
)}</mwc-button
>
</div>`
: ""}
${this._numHiddenDevices && !activeFilters
? html`<div class="active-filters">
${this.narrow
? html` <div>
<ha-icon icon="hass:filter-variant"></ha-icon>
<paper-tooltip animation-delay="0" position="left">
${this.hass.localize(
"ui.panel.config.devices.picker.filter.hidden_devices",
"number",
this._numHiddenDevices
)}
</paper-tooltip>
</div>`
: `${this.hass.localize(
"ui.panel.config.devices.picker.filter.hidden_devices",
"number",
this._numHiddenDevices
)}`}
<mwc-button @click=${this._showAll}
>${this.hass.localize(
"ui.panel.config.devices.picker.filter.show_all"
)}</mwc-button
>
</div>`
: ""}
<ha-button-menu corner="BOTTOM_START" multi>
<mwc-icon-button
slot="trigger"
.label=${this.hass!.localize(
"ui.panel.config.devices.picker.filter.filter"
)}
.title=${this.hass!.localize(
"ui.panel.config.devices.picker.filter.filter"
)}
>
<ha-svg-icon .path=${mdiFilterVariant}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item
@request-selected="${this._showDisabledChanged}"
graphic="control"
.selected=${this._showDisabled}
>
<ha-checkbox
slot="graphic"
.checked=${this._showDisabled}
></ha-checkbox>
${this.hass!.localize(
"ui.panel.config.devices.picker.filter.show_disabled"
)}
</mwc-list-item>
</ha-button-menu>
`;
return html`
<hass-tabs-subpage-data-table
@@ -469,9 +311,13 @@ export class HaConfigDeviceDashboard extends LitElement {
: "/config"}
.tabs=${configSections.integrations}
.route=${this.route}
.columns=${this._columns(this.narrow, this._showDisabled)}
.columns=${this._columns(this.narrow)}
.data=${devicesOutput}
.filter=${this._filter}
.activeFilters=${this._activeFilters(
this.entries,
this._searchParms,
this.hass.localize
)}
@row-click=${this._handleRowClicked}
clickable
.hasFab=${includeZHAFab}
@@ -487,15 +333,6 @@ export class HaConfigDeviceDashboard extends LitElement {
</ha-fab>
</a>`
: html``}
<div
class=${classMap({
"search-toolbar": this.narrow,
"table-header": !this.narrow,
})}
slot="header"
>
${headerToolbar}
</div>
</hass-tabs-subpage-data-table>
`;
}
@@ -526,136 +363,6 @@ export class HaConfigDeviceDashboard extends LitElement {
const deviceId = ev.detail.id;
navigate(this, `/config/devices/device/${deviceId}`);
}
private _showDisabledChanged(ev: CustomEvent<RequestSelectedDetail>) {
if (ev.detail.source !== "property") {
return;
}
this._showDisabled = ev.detail.selected;
}
private _handleSearchChange(ev: CustomEvent) {
this._filter = ev.detail.value;
}
private _clearFilter() {
navigate(this, window.location.pathname, true);
}
private _showAll() {
this._showDisabled = true;
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
hass-loading-screen {
--app-header-background-color: var(--sidebar-background-color);
--app-header-text-color: var(--sidebar-text-color);
}
a {
color: var(--primary-color);
}
h2 {
margin-top: 0;
font-family: var(--paper-font-headline_-_font-family);
-webkit-font-smoothing: var(
--paper-font-headline_-_-webkit-font-smoothing
);
font-size: var(--paper-font-headline_-_font-size);
font-weight: var(--paper-font-headline_-_font-weight);
letter-spacing: var(--paper-font-headline_-_letter-spacing);
line-height: var(--paper-font-headline_-_line-height);
opacity: var(--dark-primary-opacity);
}
p {
font-family: var(--paper-font-subhead_-_font-family);
-webkit-font-smoothing: var(
--paper-font-subhead_-_-webkit-font-smoothing
);
font-weight: var(--paper-font-subhead_-_font-weight);
line-height: var(--paper-font-subhead_-_line-height);
}
ha-data-table {
width: 100%;
--data-table-border-width: 0;
}
:host(:not([narrow])) ha-data-table {
height: calc(100vh - 1px - var(--header-height));
display: block;
}
ha-button-menu {
margin-right: 8px;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(var(--rgb-primary-text-color), 0.12);
}
search-input {
margin-left: 16px;
flex-grow: 1;
position: relative;
top: 2px;
}
.search-toolbar search-input {
margin-left: 8px;
top: 1px;
}
.search-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
color: var(--secondary-text-color);
}
.search-toolbar ha-button-menu {
position: static;
}
.selected-txt {
font-weight: bold;
padding-left: 16px;
}
.table-header .selected-txt {
margin-top: 20px;
}
.search-toolbar .selected-txt {
font-size: 16px;
}
.header-btns > mwc-button,
.header-btns > ha-icon-button {
margin: 8px;
}
.active-filters {
color: var(--primary-text-color);
position: relative;
display: flex;
align-items: center;
padding: 2px 2px 2px 8px;
margin-left: 4px;
font-size: 14px;
}
.active-filters ha-icon {
color: var(--primary-color);
}
.active-filters mwc-button {
margin-left: 8px;
}
.active-filters::before {
background-color: var(--primary-color);
opacity: 0.12;
border-radius: 4px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
content: "";
}
`,
];
}
}
declare global {

View File

@@ -111,19 +111,10 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
return html`
${!stateObj
? html`
<div class="container warning">
<div class="container">
${this.hass!.localize(
"ui.dialogs.entity_registry.editor.unavailable"
)}
${this._device?.disabled_by
? html`<br />${this.hass!.localize(
"ui.dialogs.entity_registry.editor.device_disabled"
)}<br /><mwc-button @click=${this._openDeviceSettings}>
${this.hass!.localize(
"ui.dialogs.entity_registry.editor.open_device_settings"
)}
</mwc-button>`
: ""}
</div>
`
: ""}
@@ -170,7 +161,6 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
<div class="row">
<ha-switch
.checked=${!this._disabledBy}
.disabled=${this._device?.disabled_by}
@change=${this._disabledByChanged}
>
</ha-switch>

View File

@@ -189,11 +189,9 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
? (name, entity: any) =>
html`
${name}<br />
<div class="secondary">
${entity.entity_id} |
${this.hass.localize(`component.${entity.platform}.title`) ||
entity.platform}
</div>
${entity.entity_id} |
${this.hass.localize(`component.${entity.platform}.title`) ||
entity.platform}
`
: undefined,
},

View File

@@ -5,8 +5,6 @@ import { classMap } from "lit-html/directives/class-map";
export class HaConfigSection extends LitElement {
@property() public isWide = false;
@property({ type: Boolean }) public vertical = false;
protected render() {
return html`
<div
@@ -18,8 +16,8 @@ export class HaConfigSection extends LitElement {
<div
class="together layout ${classMap({
narrow: !this.isWide,
vertical: this.vertical || !this.isWide,
horizontal: !this.vertical && this.isWide,
vertical: !this.isWide,
horizontal: this.isWide,
})}"
>
<div class="intro"><slot name="introduction"></slot></div>

View File

@@ -68,8 +68,6 @@ class DialogPersonDetail extends LitElement {
@internalProperty() private _submitting = false;
@internalProperty() private _personExists = false;
private _deviceTrackersAvailable = memoizeOne((hass) => {
return Object.keys(hass.states).some(
(entityId) =>
@@ -81,7 +79,6 @@ class DialogPersonDetail extends LitElement {
this._params = params;
this._error = undefined;
if (this._params.entry) {
this._personExists = true;
this._name = this._params.entry.name || "";
this._userId = this._params.entry.user_id || undefined;
this._deviceTrackers = this._params.entry.device_trackers || [];
@@ -91,7 +88,6 @@ class DialogPersonDetail extends LitElement {
: undefined;
this._isAdmin = this._user?.group_ids.includes(SYSTEM_GROUP_ID_ADMIN);
} else {
this._personExists = false;
this._name = "";
this._userId = undefined;
this._user = undefined;
@@ -402,7 +398,6 @@ class DialogPersonDetail extends LitElement {
await this._params!.updateEntry(values);
} else {
await this._params!.createEntry(values);
this._personExists = true;
}
this._params = undefined;
} catch (err) {
@@ -427,14 +422,6 @@ class DialogPersonDetail extends LitElement {
}
private _close(): void {
// If we do not have a person ID yet (= person creation dialog was just cancelled), but
// we already created a user ID for it, delete it now to not have it "free floating".
if (!this._personExists && this._userId) {
deleteUser(this.hass, this._userId);
this._params?.refreshUsers();
this._userId = undefined;
}
this._params = undefined;
}

View File

@@ -241,7 +241,7 @@ export class DialogAddUser extends LitElement {
user = userResponse.user;
} catch (err) {
this._loading = false;
this._error = err.message;
this._error = err.code;
return;
}
@@ -255,7 +255,7 @@ export class DialogAddUser extends LitElement {
} catch (err) {
await deleteUser(this.hass, user.id);
this._loading = false;
this._error = err.message;
this._error = err.code;
return;
}

View File

@@ -13,7 +13,6 @@ import {
} from "lit-element";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-help-tooltip";
import "../../../components/ha-formfield";
import "../../../components/ha-switch";
import { adminChangePassword } from "../../../data/auth";
@@ -38,8 +37,6 @@ class DialogUserDetail extends LitElement {
@internalProperty() private _isAdmin?: boolean;
@internalProperty() private _isActive?: boolean;
@internalProperty() private _error?: string;
@internalProperty() private _params?: UserDetailDialogParams;
@@ -51,7 +48,6 @@ class DialogUserDetail extends LitElement {
this._error = undefined;
this._name = params.entry.name || "";
this._isAdmin = params.entry.group_ids.includes(SYSTEM_GROUP_ID_ADMIN);
this._isActive = params.entry.is_active;
await this.updateComplete;
}
@@ -95,6 +91,15 @@ class DialogUserDetail extends LitElement {
</span>
`
: ""}
${user.is_active
? html`
<span class="state"
>${this.hass.localize(
"ui.panel.config.users.editor.active"
)}</span
>
`
: ""}
</div>
<div class="form">
<paper-input
@@ -105,21 +110,17 @@ class DialogUserDetail extends LitElement {
"ui.panel.config.users.editor.name"
)}"
></paper-input>
<div class="row">
<ha-formfield
.label=${this.hass.localize(
"ui.panel.config.users.editor.admin"
)}
.dir=${computeRTLDirection(this.hass)}
<ha-formfield
.label=${this.hass.localize("ui.panel.config.users.editor.admin")}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.disabled=${user.system_generated || user.is_owner}
.checked=${this._isAdmin}
@change=${this._adminChanged}
>
<ha-switch
.disabled=${user.system_generated || user.is_owner}
.checked=${this._isAdmin}
@change=${this._adminChanged}
>
</ha-switch>
</ha-formfield>
</div>
</ha-switch>
</ha-formfield>
${!this._isAdmin
? html`
<br />
@@ -128,27 +129,6 @@ class DialogUserDetail extends LitElement {
)}
`
: ""}
<div class="row">
<ha-formfield
.label=${this.hass.localize(
"ui.panel.config.users.editor.active"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.disabled=${user.system_generated || user.is_owner}
.checked=${this._isActive}
@change=${this._activeChanged}
>
</ha-switch>
</ha-formfield>
<ha-help-tooltip
.label=${this.hass.localize(
"ui.panel.config.users.editor.active_tooltip"
)}
>
</ha-help-tooltip>
</div>
</div>
</div>
@@ -212,16 +192,11 @@ class DialogUserDetail extends LitElement {
this._isAdmin = ev.target.checked;
}
private async _activeChanged(ev): Promise<void> {
this._isActive = ev.target.checked;
}
private async _updateEntry() {
this._submitting = true;
try {
await this._params!.updateEntry({
name: this._name.trim(),
is_active: this._isActive,
group_ids: [
this._isAdmin ? SYSTEM_GROUP_ID_ADMIN : SYSTEM_GROUP_ID_USER,
],
@@ -318,13 +293,8 @@ class DialogUserDetail extends LitElement {
.state:not(:first-child) {
margin-left: 8px;
}
.row {
display: flex;
padding: 8px 0;
}
ha-help-tooltip {
margin-left: 4px;
position: relative;
ha-switch {
margin-top: 8px;
}
`,
];

View File

@@ -46,17 +46,10 @@ export class HaConfigUsers extends LitElement {
width: "25%",
direction: "asc",
grows: true,
template: (name, user: any) =>
narrow
? html` ${name}<br />
<div class="secondary">
${user.username} |
${this.hass.localize(`groups.${user.group_ids[0]}`)}
</div>`
: html` ${name ||
this.hass!.localize(
"ui.panel.config.users.editor.unnamed_user"
)}`,
template: (name) => html`
${name ||
this.hass!.localize("ui.panel.config.users.editor.unnamed_user")}
`,
},
username: {
title: this.hass.localize(
@@ -66,7 +59,6 @@ export class HaConfigUsers extends LitElement {
filterable: true,
width: "20%",
direction: "asc",
hidden: narrow,
template: (username) => html`
${username ||
this.hass!.localize("ui.panel.config.users.editor.unnamed_user")}
@@ -79,24 +71,13 @@ export class HaConfigUsers extends LitElement {
sortable: true,
filterable: true,
width: "20%",
direction: "asc",
hidden: narrow,
template: (groupIds) => html`
${this.hass.localize(`groups.${groupIds[0]}`)}
`,
},
is_active: {
title: this.hass.localize(
"ui.panel.config.users.picker.headers.is_active"
),
type: "icon",
sortable: true,
filterable: true,
width: "80px",
template: (is_active) =>
is_active ? html`<ha-icon icon="hass:check"> </ha-icon>` : "",
},
system_generated: {
};
if (!narrow) {
columns.system_generated = {
title: this.hass.localize(
"ui.panel.config.users.picker.headers.system"
),
@@ -106,9 +87,8 @@ export class HaConfigUsers extends LitElement {
width: "160px",
template: (generated) =>
generated ? html`<ha-icon icon="hass:check"> </ha-icon>` : "",
},
};
};
}
return columns;
}
);

View File

@@ -109,7 +109,7 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
}
public setConfig(config: EntitiesCardConfig): void {
if (!config.entities || !Array.isArray(config.entities)) {
if (!config || !config.entities.length) {
throw new Error("Entities must be specified");
}

View File

@@ -1,16 +1,16 @@
import { safeDump } from "js-yaml";
import {
css,
CSSResult,
customElement,
html,
LitElement,
internalProperty,
LitElement,
TemplateResult,
} from "lit-element";
import { HomeAssistant } from "../../../types";
import { LovelaceCard } from "../types";
import { ErrorCardConfig } from "./types";
import { safeDump } from "js-yaml";
@customElement("hui-error-card")
export class HuiErrorCard extends LitElement implements LovelaceCard {
@@ -49,6 +49,7 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
font-weight: 500;
user-select: text;
cursor: default;
overflow: hidden;
}
pre {
font-family: var(--code-font-family, monospace);

View File

@@ -16,6 +16,7 @@ import "../../../components/state-history-charts";
import { CacheConfig, getRecentWithCache } from "../../../data/cached-history";
import { HistoryResult } from "../../../data/history";
import { HomeAssistant } from "../../../types";
import { findEntities } from "../common/find-entites";
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
import { processConfigEntities } from "../common/process-config-entities";
import { EntityConfig } from "../entity-rows/types";
@@ -29,9 +30,22 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
return document.createElement("hui-history-graph-card-editor");
}
public static getStubConfig(): HistoryGraphCardConfig {
// Hard coded to sun.sun to prevent high server load when it would pick an entity with a lot of state changes
return { type: "history-graph", entities: ["sun.sun"] };
public static getStubConfig(
hass: HomeAssistant,
entities: string[],
entitiesFallback: string[]
): HistoryGraphCardConfig {
const includeDomains = ["sensor"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
maxEntities,
entities,
entitiesFallback,
includeDomains
);
return { type: "history-graph", entities: foundEntities };
}
@property({ attribute: false }) public hass?: HomeAssistant;
@@ -57,12 +71,12 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
}
public setConfig(config: HistoryGraphCardConfig): void {
if (!config.entities || !Array.isArray(config.entities)) {
throw new Error("Entities need to be an array");
if (!config.entities.length) {
throw new Error("Entities must be specified");
}
if (!config.entities.length) {
throw new Error("You must include at least one entity");
if (config.entities && !Array.isArray(config.entities)) {
throw new Error("Entities need to be an array");
}
this._config = config;

View File

@@ -29,7 +29,7 @@ class HuiHorizontalStackCard extends HuiStackCard {
}
#root > * {
flex: 1 1 0;
margin: var(--horizontal-stack-card-margin, var(--stack-card-margin, 0 4px));
margin: 0 4px;
min-width: 0;
}
#root > *:first-child {

View File

@@ -246,73 +246,78 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
${!isUnavailable &&
(mediaDescription || stateObj.attributes.media_title || showControls)
? html`
<div>
<div class="title-controls">
${!mediaDescription && !stateObj.attributes.media_title
? ""
: html`
<div class="media-info">
<hui-marquee
.text=${stateObj.attributes.media_title ||
mediaDescription}
.active=${this._marqueeActive}
@mouseover=${this._marqueeMouseOver}
@mouseleave=${this._marqueeMouseLeave}
></hui-marquee>
${!stateObj.attributes.media_title
? ""
: mediaDescription}
</div>
`}
${!showControls
? ""
: html`
<div class="controls">
${controls!.map(
(control) => html`
<ha-icon-button
.title=${this.hass.localize(
`ui.card.media_player.${control.action}`
)}
.icon=${control.icon}
action=${control.action}
@click=${this._handleClick}
></ha-icon-button>
`
)}
${supportsFeature(stateObj, SUPPORT_BROWSE_MEDIA)
? html`
<mwc-icon-button
class="browse-media"
.title=${this.hass.localize(
"ui.card.media_player.browse_media"
)}
@click=${this._handleBrowseMedia}
><ha-svg-icon
.path=${mdiPlayBoxMultiple}
></ha-svg-icon
></mwc-icon-button>
`
: ""}
</div>
`}
</div>
${!this._showProgressBar
<div
class="title-controls"
style=${styleMap({
paddingRight: isOffState
? "0"
: `${this._cardHeight - 40}px`,
})}
>
${!mediaDescription && !stateObj.attributes.media_title
? ""
: html`
<paper-progress
.max=${stateObj.attributes.media_duration}
style=${styleMap({
"--paper-progress-active-color":
this._foregroundColor || "var(--accent-color)",
cursor: supportsFeature(stateObj, SUPPORT_SEEK)
? "pointer"
: "initial",
})}
@click=${this._handleSeek}
></paper-progress>
<div class="media-info">
<hui-marquee
.text=${stateObj.attributes.media_title ||
mediaDescription}
.active=${this._marqueeActive}
@mouseover=${this._marqueeMouseOver}
@mouseleave=${this._marqueeMouseLeave}
></hui-marquee>
${!stateObj.attributes.media_title
? ""
: mediaDescription}
</div>
`}
${!showControls
? ""
: html`
<div class="controls">
${controls!.map(
(control) => html`
<ha-icon-button
.title=${this.hass.localize(
`ui.card.media_player.${control.action}`
)}
.icon=${control.icon}
action=${control.action}
@click=${this._handleClick}
></ha-icon-button>
`
)}
${supportsFeature(stateObj, SUPPORT_BROWSE_MEDIA)
? html`
<mwc-icon-button
class="browse-media"
.title=${this.hass.localize(
"ui.card.media_player.browse_media"
)}
@click=${this._handleBrowseMedia}
><ha-svg-icon
.path=${mdiPlayBoxMultiple}
></ha-svg-icon
></mwc-icon-button>
`
: ""}
</div>
`}
</div>
${!this._showProgressBar
? ""
: html`
<paper-progress
.max=${stateObj.attributes.media_duration}
style=${styleMap({
"--paper-progress-active-color":
this._foregroundColor || "var(--accent-color)",
cursor: supportsFeature(stateObj, SUPPORT_SEEK)
? "pointer"
: "initial",
})}
@click=${this._handleSeek}
></paper-progress>
`}
`
: ""}
</div>
@@ -630,11 +635,6 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
.player {
position: relative;
padding: 16px;
height: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
color: var(--text-primary-color);
transition-property: color, padding;
transition-duration: 0.4s;
@@ -671,7 +671,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
mwc-icon-button.browse-media {
position: absolute;
right: 4px;
right: 0;
--mdc-icon-size: 24px;
}
@@ -693,7 +693,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
.more-info {
position: absolute;
top: 4px;
right: 4px;
right: 0px;
}
.media-info {

View File

@@ -29,7 +29,7 @@ class HuiVerticalStackCard extends HuiStackCard {
height: 100%;
}
#root > * {
margin: var(--vertical-stack-card-margin, var(--stack-card-margin, 4px 0));
margin: 4px 0 4px 0;
}
#root > *:first-child {
margin-top: 0;

View File

@@ -179,6 +179,9 @@ export class HuiActionEditor extends LitElement {
.dropdown {
display: flex;
}
paper-dropdown-menu {
flex-grow: 1;
}
`;
}
}

View File

@@ -1,4 +1,4 @@
import { mdiDrag } from "@mdi/js";
import { mdiDragVerticalVariant } from "@mdi/js";
import {
css,
CSSResult,
@@ -68,7 +68,7 @@ export class HuiEntityEditor extends LitElement {
: this.entities!.map((entityConf, index) => {
return html`
<div class="entity" data-entity-id=${entityConf.entity}>
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
<ha-svg-icon .path=${mdiDragVerticalVariant}></ha-svg-icon>
<ha-entity-picker
.hass=${this.hass}
.value=${entityConf.entity}
@@ -192,6 +192,7 @@ export class HuiEntityEditor extends LitElement {
.entity ha-svg-icon {
padding-right: 8px;
cursor: move;
color: var(--secondary-text-color);
}
.entity ha-entity-picker {
flex-grow: 1;

View File

@@ -72,7 +72,7 @@ class HuiMarquee extends LitElement {
display: flex;
position: relative;
align-items: center;
height: 1.2em;
height: 1em;
contain: strict;
}

View File

@@ -26,11 +26,7 @@ export class HuiThemeSelectEditor extends LitElement {
return html`
<paper-dropdown-menu
.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.panel.lovelace.editor.card.generic.theme")}
dynamic-align
>
<paper-listbox

View File

@@ -353,9 +353,11 @@ export class HuiCardPicker extends LitElement {
max-width: 500px;
display: flex;
flex-direction: column;
border-radius: var(--ha-card-border-radius, 4px);
border-radius: 4px;
border: 1px solid var(--divider-color);
background: var(--primary-background-color, #fafafa);
cursor: pointer;
box-sizing: border-box;
position: relative;
}
@@ -373,6 +375,7 @@ export class HuiCardPicker extends LitElement {
--ha-card-background,
var(--card-background-color, white)
);
border-radius: 0 0 4px 4px;
border-bottom: 1px solid var(--divider-color);
}
@@ -405,10 +408,6 @@ export class HuiCardPicker extends LitElement {
width: 100%;
height: 100%;
z-index: 1;
box-sizing: border-box;
border: var(--ha-card-border-width, 1px) solid
var(--ha-card-border-color, var(--divider-color));
border-radius: var(--ha-card-border-radius, 4px);
}
.manual {

View File

@@ -1,5 +1,14 @@
import { mdiHelpCircle } from "@mdi/js";
import { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
import {
mdiCheckboxBlankOutline,
mdiCheckBoxOutline,
mdiCodeBracesBox,
mdiDotsVertical,
mdiFormSelect,
mdiHelpCircleOutline,
} from "@mdi/js";
import deepFreeze from "deep-freeze";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
CSSResultArray,
@@ -14,10 +23,16 @@ import {
} from "lit-element";
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
import { fireEvent } from "../../../../common/dom/fire_event";
import { shouldHandleRequestSelectedEvent } from "../../../../common/mwc/handle-request-selected-event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/ha-circular-progress";
import "../../../../components/ha-dialog";
import "../../../../components/ha-header-bar";
import "../../../../components/ha-menu-button";
import {
CoreFrontendUserData,
getOptimisticFrontendUserDataCollection,
} from "../../../../data/frontend";
import type {
LovelaceCardConfig,
LovelaceViewConfig,
@@ -40,6 +55,7 @@ declare global {
// for fire event
interface HASSDomEvents {
"reload-lovelace": undefined;
"scroll-to-pos": { x: number; y: number };
}
// for add event listener
interface HTMLElementEventMap {
@@ -75,6 +91,12 @@ export class HuiDialogEditCard extends LitElement
@internalProperty() private _dirty = false;
@internalProperty() private _coreUserData?: CoreFrontendUserData | null;
@internalProperty() private _isAdvanced? = false;
private _unsubCoreData?: UnsubscribeFunc;
public async showDialog(params: EditCardDialogParams): Promise<void> {
this._params = params;
this._GUImode = true;
@@ -90,6 +112,13 @@ export class HuiDialogEditCard extends LitElement
if (params.cardConfig) {
this._dirty = true;
}
this._unsubCoreData = getOptimisticFrontendUserDataCollection(
this.hass.connection,
"core"
).subscribe((coreUserData) => {
this._coreUserData = coreUserData;
this._isAdvanced = coreUserData?.showAdvanced;
});
}
public closeDialog(): boolean {
@@ -102,6 +131,10 @@ export class HuiDialogEditCard extends LitElement
this._error = undefined;
this._documentationURL = undefined;
this._dirty = false;
if (this._unsubCoreData) {
this._unsubCoreData();
this._unsubCoreData = undefined;
}
fireEvent(this, "dialog-closed", { dialog: this.localName });
return true;
}
@@ -157,10 +190,11 @@ export class HuiDialogEditCard extends LitElement
<ha-dialog
open
scrimClickAction
full
.heading=${true}
@keydown=${this._ignoreKeydown}
@closed=${this._cancel}
@opened=${this._opened}
.heading=${true}
>
<div slot="heading">
<ha-header-bar>
@@ -177,11 +211,75 @@ export class HuiDialogEditCard extends LitElement
dir=${computeRTLDirection(this.hass)}
>
<mwc-icon-button>
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
<ha-svg-icon .path=${mdiHelpCircleOutline}></ha-svg-icon>
</mwc-icon-button>
</a>
`
: ""}
${this._cardConfig !== undefined
? html`
<ha-button-menu
fixed
corner="BOTTOM_START"
slot="actionItems"
@closed=${(ev) => ev.stopPropagation()}
>
<mwc-icon-button
slot="trigger"
.label=${this.hass!.localize(
"ui.panel.lovelace.editor.menu.open"
)}
.title=${this.hass!.localize(
"ui.panel.lovelace.editor.menu.open"
)}
>
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
${this._coreUserData?.showAdvanced === true
? html`
<mwc-list-item
graphic="icon"
.label=${this.hass!.localize(
!this._cardEditorEl || this._GUImode
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
)}
.disabled=${!this._guiModeAvailable}
@request-selected=${this._toggleMode}
>
<span
>${this.hass!.localize(
!this._cardEditorEl || this._GUImode
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
)}</span
>
<ha-svg-icon
slot="graphic"
.path=${!this._cardEditorEl || this._GUImode
? mdiCodeBracesBox
: mdiFormSelect}
></ha-svg-icon>
</mwc-list-item>
`
: html`
<mwc-list-item
graphic="icon"
label="Show Advanced Options"
@request-selected=${this._toggleAdvanced}
>
<span>Show Advanced Options</span>
<ha-svg-icon
slot="graphic"
.path=${this._isAdvanced
? mdiCheckBoxOutline
: mdiCheckboxBlankOutline}
></ha-svg-icon>
</mwc-list-item>
`}
</ha-button-menu>
`
: ""}
</ha-header-bar>
</div>
<div class="content">
@@ -190,9 +288,11 @@ export class HuiDialogEditCard extends LitElement
.hass=${this.hass}
.lovelace=${this._params.lovelaceConfig}
.value=${this._cardConfig}
.isAdvanced=${this._isAdvanced}
@config-changed=${this._handleConfigChanged}
@GUImode-changed=${this._handleGUIModeChanged}
@editor-save=${this._save}
@scroll-to-pos=${this._scrollToPos}
></hui-card-element-editor>
</div>
<div class="element-preview">
@@ -211,22 +311,6 @@ export class HuiDialogEditCard extends LitElement
: ``}
</div>
</div>
${this._cardConfig !== undefined
? html`
<mwc-button
slot="secondaryAction"
@click=${this._toggleMode}
.disabled=${!this._guiModeAvailable}
class="gui-mode-button"
>
${this.hass!.localize(
!this._cardEditorEl || this._GUImode
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
)}
</mwc-button>
`
: ""}
<div slot="primaryAction" @click=${this._save}>
<mwc-button @click=${this._cancel}>
${this.hass!.localize("ui.common.cancel")}
@@ -264,6 +348,13 @@ export class HuiDialogEditCard extends LitElement
ev.stopPropagation();
}
private _scrollToPos(ev: CustomEvent): void {
this.shadowRoot!.querySelector("ha-dialog")?.scrollToPos(
ev.detail.x,
ev.detail.y
);
}
private _handleConfigChanged(ev: HASSDomEvent<ConfigChangedEvent>) {
this._cardConfig = deepFreeze(ev.detail.config);
this._error = ev.detail.error;
@@ -277,10 +368,20 @@ export class HuiDialogEditCard extends LitElement
this._guiModeAvailable = ev.detail.guiModeAvailable;
}
private _toggleMode(): void {
private _toggleMode(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
this._cardEditorEl?.toggleMode();
}
private _toggleAdvanced(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
this._isAdvanced = !this._isAdvanced;
}
private _opened() {
this._cardEditorEl?.refreshYamlEditor();
}
@@ -358,37 +459,47 @@ export class HuiDialogEditCard extends LitElement
css`
:host {
--code-mirror-max-height: calc(100vh - 176px);
--dialog-content-padding: 0px 24px;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
/* overrule the ha-style-dialog max-height on small screens */
ha-dialog {
--mdc-dialog-max-height: 100%;
height: 100%;
}
}
@media all and (min-width: 850px) {
ha-dialog {
--mdc-dialog-min-width: 845px;
--mdc-dialog-max-height: calc(100% - 72px);
}
}
ha-dialog {
--mdc-dialog-max-width: 845px;
--dialog-z-index: 5;
}
@media all and (min-width: 451px) and (min-height: 501px) {
ha-dialog {
--mdc-dialog-max-width: 90vw;
}
:host([large]) .content {
:host([large]) ha-dialog {
width: calc(90vw - 48px);
}
}
@media all and (min-width: 850px) {
ha-dialog {
--mdc-dialog-min-width: 845px;
}
}
@media (min-width: 1200px) {
ha-dialog {
--mdc-dialog-max-width: 1250px;
--dialog-surface-position: fixed;
}
}
ha-dialog {
--dialog-z-index: 5;
--mdc-dialog-max-height: 750px;
}
:host([large]) ha-dialog {
--mdc-dialog-max-width: calc(100% - 32px);
}
ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
@@ -407,23 +518,17 @@ export class HuiDialogEditCard extends LitElement
flex-direction: column;
margin: 0 -10px;
}
.content hui-card-preview {
margin: 4px auto;
max-width: 390px;
}
.content .element-editor {
margin: 0 10px;
}
@media (min-width: 1200px) {
ha-dialog {
--mdc-dialog-max-width: calc(100% - 32px);
--mdc-dialog-min-width: 1000px;
--dialog-surface-position: fixed;
--dialog-surface-top: 40px;
--mdc-dialog-max-height: calc(100% - 72px);
}
.content {
flex-direction: row;
}
@@ -435,32 +540,34 @@ export class HuiDialogEditCard extends LitElement
}
.content hui-card-preview {
padding: 8px 10px;
margin: auto 0px;
margin: auto;
max-width: 500px;
}
}
.hidden {
display: none;
}
.element-editor {
margin-bottom: 8px;
}
.blur {
filter: blur(2px) grayscale(100%);
}
.element-preview {
position: relative;
height: max-content;
background: var(--primary-background-color);
padding: 4px;
border-radius: 4px;
}
.element-preview ha-circular-progress {
top: 50%;
left: 50%;
position: absolute;
z-index: 10;
}
hui-card-preview {
padding-top: 8px;
margin-bottom: 4px;
@@ -468,14 +575,13 @@ export class HuiDialogEditCard extends LitElement
width: 100%;
box-sizing: border-box;
}
.gui-mode-button {
margin-right: auto;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
}
.header_button {
color: inherit;
text-decoration: none;

View File

@@ -14,4 +14,13 @@ export const configElementStyle = css`
.suffix {
margin: 0 8px;
}
ha-settings-row {
padding: 0;
}
ha-expansion-panel {
padding-top: 8px;
}
ha-expansion-panel .title {
font-size: 16px;
}
`;

View File

@@ -19,6 +19,7 @@ import { HomeAssistant } from "../../../../types";
import { AlarmPanelCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -37,6 +38,8 @@ export class HuiAlarmPanelCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: AlarmPanelCardConfig;
public setConfig(config: AlarmPanelCardConfig): void {
@@ -68,62 +71,68 @@ export class HuiAlarmPanelCardEditor extends LitElement
const states = ["arm_home", "arm_away", "arm_night", "arm_custom_bypass"];
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value="${this._entity}"
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change="${this._valueChanged}"
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._name}"
.configValue="${"name"}"
@value-changed="${this._valueChanged}"
></paper-input>
<span>Used States</span> ${this._states.map((state, index) => {
return html`
<div class="states">
<paper-item>${state}</paper-item>
<ha-icon
class="deleteState"
.value="${index}"
icon="hass:close"
@click=${this._stateRemoved}
></ha-icon>
</div>
`;
})}
<paper-dropdown-menu
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.alarm-panel.available_states"
)}"
@value-changed="${this._stateAdded}"
>
<paper-listbox slot="dropdown-content">
${states.map((state) => {
return html` <paper-item>${state}</paper-item> `;
})}
</paper-listbox>
</paper-dropdown-menu>
<hui-theme-select-editor
.hass=${this.hass}
.value="${this._theme}"
.configValue="${"theme"}"
@value-changed="${this._valueChanged}"
></hui-theme-select-editor>
</div>
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
allow-custom-entity
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change=${this._valueChanged}
></ha-entity-picker>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
<span>Used States</span>
${this._states.map((state, index) => {
return html`
<div class="states">
<paper-item>${state}</paper-item>
<ha-icon
class="deleteState"
.value=${index}
icon="hass:close"
@click=${this._stateRemoved}
></ha-icon>
</div>
`;
})}
<paper-dropdown-menu
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.alarm-panel.available_states"
)}
@value-changed=${this._stateAdded}
>
<paper-listbox slot="dropdown-content">
${states.map((state) => {
return html`<paper-item>${state}</paper-item>`;
})}
</paper-listbox>
</paper-dropdown-menu>
</div>
<div slot="advanced" class="card-config">
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</hui-config-element-template>
`;
}

View File

@@ -11,9 +11,9 @@ import {
import { assert, boolean, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stateIcon } from "../../../../common/entity/state_icon";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/ha-formfield";
import "../../../../components/ha-icon-input";
import "../../../../components/ha-settings-row";
import "../../../../components/ha-switch";
import { ActionConfig } from "../../../../data/lovelace";
import { HomeAssistant } from "../../../../types";
@@ -22,6 +22,7 @@ import "../../components/hui-action-editor";
import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { actionConfigStruct, EditorTarget } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -53,6 +54,8 @@ export class HuiButtonCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: ButtonCardConfig;
public setConfig(config: ButtonCardConfig): void {
@@ -107,117 +110,34 @@ export class HuiButtonCardEditor extends LitElement
return html``;
}
const dir = computeRTLDirection(this.hass!);
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
@value-changed=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<div class="side-by-side">
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
allow-custom-entity
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)}
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
@value-changed=${this._valueChanged}
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-icon-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.icon"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._icon}
.placeholder=${this._icon ||
stateIcon(this.hass.states[this._entity])}
.configValue=${"icon"}
@value-changed=${this._valueChanged}
></ha-icon-input>
</div>
<div class="side-by-side">
<div>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_name"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._show_name !== false}
.configValue=${"show_name"}
@change=${this._change}
></ha-switch>
</ha-formfield>
</div>
<div>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_state"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._show_state !== false}
.configValue=${"show_state"}
@change=${this._change}
></ha-switch>
</ha-formfield>
</div>
<div>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_icon"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._show_icon !== false}
.configValue=${"show_icon"}
@change=${this._change}
></ha-switch>
</ha-formfield>
</div>
</div>
<div class="side-by-side">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.icon_height"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._icon_height}
.configValue=${"icon_height"}
@value-changed=${this._valueChanged}
type="number"
><div class="suffix" slot="suffix">px</div>
</paper-input>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
<div class="side-by-side">
<hui-action-editor
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.tap_action"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.hass=${this.hass}
.config=${this._tap_action}
.actions=${actions}
@@ -227,12 +147,40 @@ export class HuiButtonCardEditor extends LitElement
)}
@value-changed=${this._valueChanged}
></hui-action-editor>
</div>
<div slot="advanced" class="card-config">
<div class="side-by-side">
<ha-icon-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.icon"
)}
.value=${this._icon}
.placeholder=${this._icon ||
stateIcon(this.hass.states[this._entity])}
.configValue=${"icon"}
@value-changed=${this._valueChanged}
></ha-icon-input>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.icon_height"
)}
.value=${this._icon_height}
.configValue=${"icon_height"}
@value-changed=${this._valueChanged}
type="number"
><div class="suffix" slot="suffix">px</div>
</paper-input>
</div>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
<hui-action-editor
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hold_action"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.hass=${this.hass}
.config=${this._hold_action}
.actions=${actions}
@@ -242,8 +190,44 @@ export class HuiButtonCardEditor extends LitElement
)}
@value-changed=${this._valueChanged}
></hui-action-editor>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_state"
)}
</span>
<ha-switch
.checked=${this._show_state !== false}
.configValue=${"show_state"}
@change=${this._change}
></ha-switch>
</ha-settings-row>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_name"
)}
</span>
<ha-switch
.checked=${this._show_name !== false}
.configValue=${"show_name"}
@change=${this._change}
></ha-switch>
</ha-settings-row>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_icon"
)}
</span>
<ha-switch
.checked=${this._show_icon !== false}
.configValue=${"show_icon"}
@change=${this._change}
></ha-switch>
</ha-settings-row>
</div>
</div>
</hui-config-element-template>
`;
}

View File

@@ -1,5 +1,6 @@
import {
CSSResult,
css,
CSSResultArray,
customElement,
html,
internalProperty,
@@ -23,6 +24,7 @@ import type { CalendarCardConfig } from "../../cards/types";
import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import type { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import type { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -41,6 +43,8 @@ export class HuiCalendarCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@property({ attribute: false }) private _config?: CalendarCardConfig;
@internalProperty() private _configEntities?: string[];
@@ -69,14 +73,15 @@ export class HuiCalendarCardEditor extends LitElement
}
return html`
<div class="card-config">
<div class="side-by-side">
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<paper-input
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
@@ -105,28 +110,30 @@ export class HuiCalendarCardEditor extends LitElement
</paper-listbox>
</paper-dropdown-menu>
</div>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
<h3>
${`${this.hass.localize(
"ui.panel.lovelace.editor.card.calendar.calendar_entities"
)} (
${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.required"
)})`}
</h3>
<ha-entities-picker
.hass=${this.hass!}
.value=${this._configEntities}
.includeDomains=${["calendar"]}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
<h3>
${this.hass.localize(
"ui.panel.lovelace.editor.card.calendar.calendar_entities"
) +
" (" +
this.hass!.localize("ui.panel.lovelace.editor.card.config.required") +
")"}
</h3>
<ha-entities-picker
.hass=${this.hass!}
.value=${this._configEntities}
.includeDomains=${["calendar"]}
@value-changed=${this._valueChanged}
>
</ha-entities-picker>
>
</ha-entities-picker>
<div slot="advanced" class="card-config">
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</hui-config-element-template>
`;
}
@@ -175,8 +182,15 @@ export class HuiCalendarCardEditor extends LitElement
fireEvent(this, "config-changed", { config: this._config });
}
static get styles(): CSSResult {
return configElementStyle;
static get styles(): CSSResultArray {
return [
configElementStyle,
css`
paper-dropdown-menu {
width: 100%;
}
`,
];
}
}

View File

@@ -22,11 +22,11 @@ import {
union,
} from "superstruct";
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/entity/state-badge";
import "../../../../components/ha-card";
import "../../../../components/ha-formfield";
import "../../../../components/ha-icon";
import "../../../../components/ha-settings-row";
import "../../../../components/ha-switch";
import type { HomeAssistant } from "../../../../types";
import type { EntitiesCardConfig } from "../../cards/types";
@@ -35,6 +35,7 @@ import type { LovelaceRowConfig } from "../../entity-rows/types";
import { headerFooterConfigStructs } from "../../header-footer/types";
import type { LovelaceCardEditor } from "../../types";
import "../header-footer-editor/hui-header-footer-editor";
import "../hui-config-element-template";
import "../hui-entities-card-row-editor";
import "../hui-sub-element-editor";
import { processEditorEntities } from "../process-editor-entities";
@@ -62,6 +63,8 @@ export class HuiEntitiesCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: EntitiesCardConfig;
@internalProperty() private _configEntities?: LovelaceRowConfig[];
@@ -92,6 +95,7 @@ export class HuiEntitiesCardEditor extends LitElement
<hui-sub-element-editor
.hass=${this.hass}
.config=${this._subElementEditorConfig}
.isAdvancedUser=${this.isAdvanced}
@go-back=${this._goBack}
@config-changed=${this._handleSubElementChanged}
>
@@ -100,70 +104,80 @@ export class HuiEntitiesCardEditor extends LitElement
}
return html`
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
<div class="side-by-side">
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.entities.show_header_toggle"
)}
.dir=${computeRTLDirection(this.hass)}
>
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-settings-row>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.entities.show_header_toggle"
)}
</span>
<span slot="description">
${this.hass.localize(
"ui.panel.lovelace.editor.card.entities.show_header_toggle_secondary"
)}
</span>
<ha-switch
.checked=${this._config!.show_header_toggle !== false}
.configValue=${"show_header_toggle"}
@change=${this._valueChanged}
></ha-switch>
</ha-formfield>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.state_color"
)}
.dir=${computeRTLDirection(this.hass)}
>
</ha-settings-row>
</div>
<hui-entities-card-row-editor
.hass=${this.hass}
.entities=${this._configEntities}
@entities-changed=${this._valueChanged}
@edit-detail-element=${this._editDetailElement}
></hui-entities-card-row-editor>
<div slot="advanced" class="card-config">
<hui-header-footer-editor
.hass=${this.hass}
.configValue=${"header"}
.config=${this._config.header}
@value-changed=${this._valueChanged}
@edit-detail-element=${this._editDetailElement}
></hui-header-footer-editor>
<hui-header-footer-editor
.hass=${this.hass}
.configValue=${"footer"}
.config=${this._config.footer}
@value-changed=${this._valueChanged}
@edit-detail-element=${this._editDetailElement}
></hui-header-footer-editor>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.state_color"
)}
</span>
<ha-switch
.checked=${this._config!.state_color}
.configValue=${"state_color"}
@change=${this._valueChanged}
></ha-switch>
</ha-formfield>
</ha-settings-row>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
<hui-header-footer-editor
.hass=${this.hass}
.configValue=${"header"}
.config=${this._config.header}
@value-changed=${this._valueChanged}
@edit-detail-element=${this._editDetailElement}
></hui-header-footer-editor>
<hui-header-footer-editor
.hass=${this.hass}
.configValue=${"footer"}
.config=${this._config.footer}
@value-changed=${this._valueChanged}
@edit-detail-element=${this._editDetailElement}
></hui-header-footer-editor>
</div>
<hui-entities-card-row-editor
.hass=${this.hass}
.entities=${this._configEntities}
@entities-changed=${this._valueChanged}
@edit-detail-element=${this._editDetailElement}
></hui-entities-card-row-editor>
</hui-config-element-template>
`;
}
@@ -188,20 +202,9 @@ export class HuiEntitiesCardEditor extends LitElement
return;
}
if (configValue === "row" || (ev.detail && ev.detail.entities)) {
if (ev.detail && ev.detail.entities) {
const newConfigEntities =
ev.detail.entities || this._configEntities!.concat();
if (configValue === "row") {
if (!value) {
newConfigEntities.splice(this._subElementEditorConfig!.index!, 1);
this._goBack();
} else {
newConfigEntities[this._subElementEditorConfig!.index!] = value;
}
this._subElementEditorConfig!.elementConfig = value;
}
this._config = { ...this._config!, entities: newConfigEntities };
this._configEntities = processEditorEntities(this._config!.entities);
} else if (configValue) {
@@ -221,18 +224,20 @@ export class HuiEntitiesCardEditor extends LitElement
private _handleSubElementChanged(ev: CustomEvent): void {
ev.stopPropagation();
if (!this._config || !this.hass) {
const configValue = this._subElementEditorConfig?.type;
if (!this._config || !this.hass || !configValue) {
return;
}
const configValue = this._subElementEditorConfig?.type;
const value = ev.detail.config;
let goBack = false;
if (configValue === "row") {
const newConfigEntities = this._configEntities!.concat();
if (!value) {
newConfigEntities.splice(this._subElementEditorConfig!.index!, 1);
this._goBack();
goBack = true;
} else {
newConfigEntities[this._subElementEditorConfig!.index!] = value;
}
@@ -240,9 +245,10 @@ export class HuiEntitiesCardEditor extends LitElement
this._config = { ...this._config!, entities: newConfigEntities };
this._configEntities = processEditorEntities(this._config!.entities);
} else if (configValue) {
if (value === "") {
if (!value || value === "") {
this._config = { ...this._config };
delete this._config[configValue!];
goBack = true;
} else {
this._config = {
...this._config,
@@ -251,16 +257,21 @@ export class HuiEntitiesCardEditor extends LitElement
}
}
this._subElementEditorConfig = {
...this._subElementEditorConfig!,
elementConfig: value,
};
if (goBack) {
this._goBack();
} else {
this._subElementEditorConfig = {
...this._subElementEditorConfig!,
elementConfig: value,
};
}
fireEvent(this, "config-changed", { config: this._config });
}
private _editDetailElement(ev: HASSDomEvent<EditSubElementEvent>): void {
this._subElementEditorConfig = ev.detail.subElementConfig;
fireEvent(this, "scroll-to-pos", { x: 0, y: 0 });
}
private _goBack(): void {

View File

@@ -20,6 +20,7 @@ import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import { headerFooterConfigStructs } from "../../header-footer/types";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -39,6 +40,8 @@ export class HuiEntityCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: EntityCardConfig;
public setConfig(config: EntityCardConfig): void {
@@ -76,74 +79,67 @@ export class HuiEntityCardEditor extends LitElement
}
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
@change=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<div class="side-by-side">
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)}
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
@change=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div slot="advanced" class="card-config">
<ha-entity-attribute-picker
.hass=${this.hass}
.entityId=${this._entity}
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.attribute"
)}
.value=${this._attribute}
.configValue=${"attribute"}
@value-changed=${this._valueChanged}
></ha-entity-attribute-picker>
<ha-icon-input
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.icon"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.value=${this._icon}
.placeholder=${this._icon ||
stateIcon(this.hass.states[this._entity])}
.configValue=${"icon"}
@value-changed=${this._valueChanged}
></ha-icon-input>
</div>
<div class="side-by-side">
<ha-entity-attribute-picker
.hass=${this.hass}
.entityId=${this._entity}
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.attribute"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._attribute}
.configValue=${"attribute"}
@value-changed=${this._valueChanged}
></ha-entity-attribute-picker>
<paper-input
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.unit"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.value=${this._unit}
.configValue=${"unit"}
@value-changed=${this._valueChanged}
></paper-input>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</hui-config-element-template>
`;
}

View File

@@ -1,7 +1,6 @@
import "@polymer/paper-input/paper-input";
import {
css,
CSSResultArray,
CSSResult,
customElement,
html,
internalProperty,
@@ -11,14 +10,15 @@ import {
} from "lit-element";
import { assert, number, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/ha-formfield";
import "../../../../components/ha-settings-row";
import "../../../../components/ha-switch";
import { HomeAssistant } from "../../../../types";
import { GaugeCardConfig, SeverityConfig } from "../../cards/types";
import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -40,6 +40,8 @@ export class HuiGaugeCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: GaugeCardConfig;
public setConfig(config: GaugeCardConfig): void {
@@ -81,141 +83,131 @@ export class HuiGaugeCardEditor extends LitElement
}
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value="${this._entity}"
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change="${this._valueChanged}"
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._name}"
.configValue=${"name"}
@value-changed="${this._valueChanged}"
></paper-input>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.unit"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._unit}"
.configValue=${"unit"}
@value-changed="${this._valueChanged}"
></paper-input>
<hui-theme-select-editor
.hass=${this.hass}
.value="${this._theme}"
.configValue="${"theme"}"
@value-changed="${this._valueChanged}"
></hui-theme-select-editor>
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.minimum"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._min}"
.configValue=${"min"}
@value-changed="${this._valueChanged}"
></paper-input>
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.maximum"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._max}"
.configValue=${"max"}
@value-changed="${this._valueChanged}"
></paper-input>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.gauge.severity.define"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked="${this._config!.severity !== undefined}"
@change="${this._toggleSeverity}"
></ha-switch
></ha-formfield>
${this._config!.severity !== undefined
? html`
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.gauge.severity.green"
)} (${this.hass.localize(
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
allow-custom-entity
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value="${this._severity ? this._severity.green : 0}"
.configValue=${"green"}
@value-changed="${this._severityChanged}"
></paper-input>
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.gauge.severity.yellow"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value="${this._severity ? this._severity.yellow : 0}"
.configValue=${"yellow"}
@value-changed="${this._severityChanged}"
></paper-input>
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.gauge.severity.red"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value="${this._severity ? this._severity.red : 0}"
.configValue=${"red"}
@value-changed="${this._severityChanged}"
></paper-input>
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change=${this._valueChanged}
></ha-entity-picker>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
<div class="side-by-side">
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.minimum"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._min}
.configValue=${"min"}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.maximum"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._max}
.configValue=${"max"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
`
: ""}
</div>
</div>
<div slot="advanced" class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.unit"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._unit}
.configValue=${"unit"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.gauge.severity.define"
)}
</span>
<ha-switch
.checked=${this._config!.severity !== undefined}
@change=${this._toggleSeverity}
></ha-switch>
</ha-settings-row>
${this._config!.severity !== undefined
? html`
<div class="side-by-side">
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.gauge.severity.green"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value=${this._severity ? this._severity.green : 0}
.configValue=${"green"}
@value-changed=${this._severityChanged}
></paper-input>
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.gauge.severity.yellow"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value=${this._severity ? this._severity.yellow : 0}
.configValue=${"yellow"}
@value-changed=${this._severityChanged}
></paper-input>
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.gauge.severity.red"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value=${this._severity ? this._severity.red : 0}
.configValue=${"red"}
@value-changed=${this._severityChanged}
></paper-input>
</div>
`
: ""}
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</hui-config-element-template>
`;
}
static get styles(): CSSResultArray {
return [
configElementStyle,
css`
.severity {
display: none;
width: 100%;
padding-left: 16px;
flex-direction: row;
flex-wrap: wrap;
}
.severity > * {
flex: 1 0 30%;
padding-right: 4px;
}
ha-switch[checked] ~ .severity {
display: flex;
}
`,
];
static get styles(): CSSResult {
return configElementStyle;
}
private _toggleSeverity(ev: EntitiesEditorEvent): void {

View File

@@ -76,6 +76,7 @@ export class HuiGenericEntityRowEditor extends LitElement
return html`
<div class="card-config">
<ha-entity-picker
hide-clear-icon
allow-custom-entity
.hass=${this.hass}
.value=${this._config.entity}

View File

@@ -21,17 +21,18 @@ import {
union,
} from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/entity/state-badge";
import "../../../../components/ha-card";
import "../../../../components/ha-formfield";
import "../../../../components/ha-icon";
import "../../../../components/ha-settings-row";
import "../../../../components/ha-switch";
import { HomeAssistant } from "../../../../types";
import { ConfigEntity, GlanceCardConfig } from "../../cards/types";
import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { processEditorEntities } from "../process-editor-entities";
import {
EditorTarget,
@@ -57,6 +58,8 @@ export class HuiGlanceCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: GlanceCardConfig;
@internalProperty() private _configEntities?: ConfigEntity[];
@@ -100,103 +103,94 @@ export class HuiGlanceCardEditor extends LitElement
return html``;
}
const dir = computeRTLDirection(this.hass!);
return html`
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<div class="side-by-side">
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)}
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<hui-entity-editor
.hass=${this.hass}
.entities=${this._configEntities}
@entities-changed=${this._valueChanged}
></hui-entity-editor>
<div slot="advanced" class="card-config">
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.glance.columns"
)}
type="number"
.value=${this._columns}
.configValue=${"columns"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_name"
)}
</span>
<ha-switch
.checked=${this._config!.show_name !== false}
.configValue=${"show_name"}
@change=${this._valueChanged}
></ha-switch>
</ha-settings-row>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_icon"
)}
</span>
<ha-switch
.checked=${this._config!.show_icon !== false}
.configValue=${"show_icon"}
@change=${this._valueChanged}
>
</ha-switch>
</ha-settings-row>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_state"
)}
</span>
<ha-switch
.checked=${this._config!.show_state !== false}
.configValue=${"show_state"}
@change=${this._valueChanged}
>
</ha-switch>
</ha-settings-row>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.state_color"
)}
</span>
<ha-switch
.checked=${this._config!.state_color}
.configValue=${"state_color"}
@change=${this._valueChanged}
></ha-switch>
</ha-settings-row>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.glance.columns"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
type="number"
.value=${this._columns}
.configValue=${"columns"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div class="side-by-side">
<div>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_name"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._config!.show_name !== false}
.configValue=${"show_name"}
@change=${this._valueChanged}
></ha-switch>
</ha-formfield>
</div>
<div>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_icon"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._config!.show_icon !== false}
.configValue=${"show_icon"}
@change=${this._valueChanged}
>
</ha-switch>
</ha-formfield>
</div>
<div>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_state"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._config!.show_state !== false}
.configValue=${"show_state"}
@change=${this._valueChanged}
>
</ha-switch>
</ha-formfield>
</div>
</div>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.state_color"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${this._config!.state_color}
.configValue=${"state_color"}
@change=${this._valueChanged}
></ha-switch>
</ha-formfield>
</div>
<hui-entity-editor
.hass=${this.hass}
.entities=${this._configEntities}
@entities-changed=${this._valueChanged}
></hui-entity-editor>
</hui-config-element-template>
`;
}

View File

@@ -24,6 +24,7 @@ import { EntityId } from "../../common/structs/is-entity-id";
import "../../components/hui-entity-editor";
import { EntityConfig } from "../../entity-rows/types";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { processEditorEntities } from "../process-editor-entities";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -49,6 +50,8 @@ export class HuiHistoryGraphCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: HistoryGraphCardConfig;
@internalProperty() private _configEntities?: EntityConfig[];
@@ -81,47 +84,48 @@ export class HuiHistoryGraphCardEditor extends LitElement
}
return html`
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._title}"
.configValue="${"title"}"
@value-changed="${this._valueChanged}"
></paper-input>
<div class="side-by-side">
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hours_to_show"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._hours_to_show}"
.configValue=${"hours_to_show"}
@value-changed="${this._valueChanged}"
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)}
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<div class="side-by-side">
<paper-input
type="number"
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hours_to_show"
)}
.value=${this._hours_to_show}
.configValue=${"hours_to_show"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<hui-entity-editor
.hass=${this.hass}
.entities=${this._configEntities}
@entities-changed=${this._valueChanged}
></hui-entity-editor>
</div>
<div slot="advanced" class="card-config">
<paper-input
type="number"
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.refresh_interval"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._refresh_interval}"
)}
.value=${this._refresh_interval}
.configValue=${"refresh_interval"}
@value-changed="${this._valueChanged}"
@value-changed=${this._valueChanged}
></paper-input>
</div>
<hui-entity-editor
.hass=${this.hass}
.entities="${this._configEntities}"
@entities-changed="${this._valueChanged}"
></hui-entity-editor>
</div>
</hui-config-element-template>
`;
}

View File

@@ -15,6 +15,7 @@ import { HomeAssistant } from "../../../../types";
import { HumidifierCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -32,6 +33,8 @@ export class HuiHumidifierCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: HumidifierCardConfig;
public setConfig(config: HumidifierCardConfig): void {
@@ -57,37 +60,42 @@ export class HuiHumidifierCardEditor extends LitElement
}
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value="${this._entity}"
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change="${this._valueChanged}"
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._name}"
.configValue="${"name"}"
@value-changed="${this._valueChanged}"
></paper-input>
<hui-theme-select-editor
.hass=${this.hass}
.value="${this._theme}"
.configValue="${"theme"}"
@value-changed="${this._valueChanged}"
></hui-theme-select-editor>
</div>
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div slot="advanced" class="card-config">
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</hui-config-element-template>
`;
}

View File

@@ -13,6 +13,7 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import { HomeAssistant } from "../../../../types";
import { IframeCardConfig } from "../../cards/types";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -28,6 +29,8 @@ export class HuiIframeCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: IframeCardConfig;
public setConfig(config: IframeCardConfig): void {
@@ -53,40 +56,41 @@ export class HuiIframeCardEditor extends LitElement
}
return html`
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.url"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value="${this._url}"
.configValue="${"url"}"
@value-changed="${this._valueChanged}"
></paper-input>
<div class="side-by-side">
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
"ui.panel.lovelace.editor.card.generic.url"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
"ui.panel.lovelace.editor.card.config.required"
)})"
.value="${this._title}"
.configValue="${"title"}"
@value-changed="${this._valueChanged}"
.value=${this._url}
.configValue=${"url"}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._aspect_ratio}"
.configValue="${"aspect_ratio"}"
@value-changed="${this._valueChanged}"
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)}
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
</div>
<div slot="advanced" class="card-config">
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)}
.value=${this._aspect_ratio}
.configValue=${"aspect_ratio"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
</hui-config-element-template>
`;
}

View File

@@ -19,6 +19,7 @@ import "../../components/hui-action-editor";
import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { actionConfigStruct, EditorTarget } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -39,6 +40,8 @@ export class HuiLightCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: LightCardConfig;
public setConfig(config: LightCardConfig): void {
@@ -85,78 +88,73 @@ export class HuiLightCardEditor extends LitElement
];
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@value-changed=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<div class="side-by-side">
<paper-input
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@value-changed=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div slot="advanced" class="card-config">
<ha-icon-input
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.icon"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.value=${this._icon}
.placeholder=${this._icon ||
stateIcon(this.hass.states[this._entity])}
.configValue=${"icon"}
@value-changed=${this._valueChanged}
></ha-icon-input>
<hui-action-editor
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hold_action"
)}
.hass=${this.hass}
.config=${this._hold_action}
.actions=${actions}
.configValue=${"hold_action"}
@value-changed=${this._valueChanged}
></hui-action-editor>
<hui-action-editor
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.double_tap_action"
)}
.hass=${this.hass}
.config=${this._double_tap_action}
.actions=${actions}
.configValue=${"double_tap_action"}
@value-changed=${this._valueChanged}
></hui-action-editor>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
<hui-action-editor
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hold_action"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.hass=${this.hass}
.config=${this._hold_action}
.actions=${actions}
.configValue=${"hold_action"}
@value-changed=${this._valueChanged}
></hui-action-editor>
<hui-action-editor
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.double_tap_action"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.hass=${this.hass}
.config=${this._double_tap_action}
.actions=${actions}
.configValue=${"double_tap_action"}
@value-changed=${this._valueChanged}
></hui-action-editor>
</div>
</hui-config-element-template>
`;
}

View File

@@ -17,6 +17,7 @@ import { LogbookCardConfig } from "../../cards/types";
import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -33,6 +34,8 @@ export class HuiLogbookCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: LogbookCardConfig;
@internalProperty() private _configEntities?: string[];
@@ -65,50 +68,51 @@ export class HuiLogbookCardEditor extends LitElement
}
return html`
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<div class="side-by-side">
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)}
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
type="number"
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hours_to_show"
)}
.value=${this._hours_to_show}
.configValue=${"hours_to_show"}
@value-changed=${this._valueChanged}
></paper-input>
<h3>
${`${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entities"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.required"
)})`}
</h3>
<ha-entities-picker
.hass=${this.hass}
.value=${this._configEntities}
@value-changed=${this._valueChanged}
>
</ha-entities-picker>
</div>
<div slot="advanced" class="card-config">
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
<paper-input
type="number"
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hours_to_show"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._hours_to_show}
.configValue=${"hours_to_show"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<h3>
${`${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entities"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.required"
)})`}
</h3>
<ha-entities-picker
.hass=${this.hass}
.value=${this._configEntities}
@value-changed=${this._valueChanged}
>
</ha-entities-picker>
</div>
</hui-config-element-template>
`;
}

View File

@@ -19,8 +19,8 @@ import {
string,
} from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/ha-formfield";
import "../../../../components/ha-settings-row";
import "../../../../components/ha-switch";
import { PolymerChangedEvent } from "../../../../polymer-types";
import { HomeAssistant } from "../../../../types";
@@ -29,6 +29,7 @@ import "../../components/hui-entity-editor";
import "../../components/hui-input-list-editor";
import { EntityConfig } from "../../entity-rows/types";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { processEditorEntities } from "../process-editor-entities";
import {
EditorTarget,
@@ -52,6 +53,8 @@ const cardConfigStruct = object({
export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: MapCardConfig;
@internalProperty() private _configEntities?: EntityConfig[];
@@ -94,87 +97,84 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
}
return html`
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._title}"
.configValue="${"title"}"
@value-changed="${this._valueChanged}"
></paper-input>
<div class="side-by-side">
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._aspect_ratio}"
.configValue="${"aspect_ratio"}"
@value-changed="${this._valueChanged}"
></paper-input>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.map.default_zoom"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
type="number"
.value="${this._default_zoom}"
.configValue="${"default_zoom"}"
@value-changed="${this._valueChanged}"
></paper-input>
</div>
<div class="side-by-side">
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.map.dark_mode"
"ui.panel.lovelace.editor.card.generic.title"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked="${this._dark_mode}"
.configValue="${"dark_mode"}"
@change="${this._valueChanged}"
></ha-switch
></ha-formfield>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.map.hours_to_show"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
type="number"
.value="${this._hours_to_show}"
.configValue="${"hours_to_show"}"
@value-changed="${this._valueChanged}"
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<hui-entity-editor
.hass=${this.hass}
.entities="${this._configEntities}"
@entities-changed="${this._entitiesValueChanged}"
></hui-entity-editor>
<h3>
${this.hass.localize(
"ui.panel.lovelace.editor.card.map.geo_location_sources"
)}
</h3>
<div class="geo_location_sources">
<hui-input-list-editor
inputLabel=${this.hass.localize(
"ui.panel.lovelace.editor.card.map.source"
<div class="side-by-side">
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.map.default_zoom"
)}
type="number"
.value=${this._default_zoom}
.configValue=${"default_zoom"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.map.hours_to_show"
)}
type="number"
.value=${this._hours_to_show}
.configValue=${"hours_to_show"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.map.dark_mode"
)}
</span>
<ha-switch
.checked=${this._dark_mode}
.configValue=${"dark_mode"}
@change=${this._valueChanged}
></ha-switch>
</ha-settings-row>
<hui-entity-editor
.hass=${this.hass}
.value="${this._geo_location_sources}"
.configValue="${"geo_location_sources"}"
@value-changed="${this._valueChanged}"
></hui-input-list-editor>
.entities=${this._configEntities}
@entities-changed=${this._entitiesValueChanged}
></hui-entity-editor>
</div>
</div>
<div slot="advanced" class="card-config">
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)}
.value=${this._aspect_ratio}
.configValue=${"aspect_ratio"}
@value-changed=${this._valueChanged}
></paper-input>
<h3>
${this.hass.localize(
"ui.panel.lovelace.editor.card.map.geo_location_sources"
)}
</h3>
<div class="geo_location_sources">
<hui-input-list-editor
inputLabel=${this.hass.localize(
"ui.panel.lovelace.editor.card.map.source"
)}
.hass=${this.hass}
.value=${this._geo_location_sources}
.configValue=${"geo_location_sources"}
@value-changed=${this._valueChanged}
></hui-input-list-editor>
</div>
</div>
</hui-config-element-template>
`;
}

View File

@@ -15,6 +15,7 @@ import { HomeAssistant } from "../../../../types";
import { MarkdownCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -30,6 +31,8 @@ export class HuiMarkdownCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: MarkdownCardConfig;
public setConfig(config: MarkdownCardConfig): void {
@@ -55,38 +58,43 @@ export class HuiMarkdownCardEditor extends LitElement
}
return html`
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._title}"
.configValue="${"title"}"
@value-changed="${this._valueChanged}"
></paper-input>
<paper-textarea
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.markdown.content"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value="${this._content}"
.configValue="${"content"}"
@keydown=${this._ignoreKeydown}
@value-changed="${this._valueChanged}"
autocapitalize="none"
autocomplete="off"
spellcheck="false"
></paper-textarea>
<hui-theme-select-editor
.hass=${this.hass}
.value="${this._theme}"
.configValue="${"theme"}"
@value-changed="${this._valueChanged}"
></hui-theme-select-editor>
</div>
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)}
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<paper-textarea
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.markdown.content"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value=${this._content}
.configValue=${"content"}
@keydown=${this._ignoreKeydown}
@value-changed=${this._valueChanged}
autocapitalize="none"
autocomplete="off"
spellcheck="false"
></paper-textarea>
</div>
<div slot="advanced" class="card-config">
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</hui-config-element-template>
`;
}

View File

@@ -1,18 +1,18 @@
import {
customElement,
html,
internalProperty,
LitElement,
property,
internalProperty,
TemplateResult,
} from "lit-element";
import { assert, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entity-picker";
import { HomeAssistant } from "../../../../types";
import { MediaControlCardConfig } from "../../cards/types";
import { LovelaceCardEditor } from "../../types";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { assert, object, string, optional } from "superstruct";
const cardConfigStruct = object({
type: string(),
@@ -45,17 +45,17 @@ export class HuiMediaControlCardEditor extends LitElement
return html`
<div class="card-config">
<ha-entity-picker
allow-custom-entity
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value="${this._entity}"
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change="${this._valueChanged}"
allow-custom-entity
@change=${this._valueChanged}
></ha-entity-picker>
</div>
`;

View File

@@ -16,6 +16,7 @@ import { PictureCardConfig } from "../../cards/types";
import "../../components/hui-action-editor";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { actionConfigStruct, EditorTarget } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -32,6 +33,8 @@ export class HuiPictureCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: PictureCardConfig;
public setConfig(config: PictureCardConfig): void {
@@ -63,50 +66,51 @@ export class HuiPictureCardEditor extends LitElement
const actions = ["navigate", "url", "call-service", "none"];
return html`
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.image"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.value="${this._image}"
.configValue="${"image"}"
@value-changed="${this._valueChanged}"
></paper-input>
<div class="side-by-side">
<hui-action-editor
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.tap_action"
"ui.panel.lovelace.editor.card.generic.image"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
"ui.panel.lovelace.editor.card.config.required"
)})"
.value=${this._image}
.configValue=${"image"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div slot="advanced" class="card-config">
<hui-action-editor
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.tap_action"
)}
.hass=${this.hass}
.config="${this._tap_action}"
.actions="${actions}"
.configValue="${"tap_action"}"
@value-changed="${this._valueChanged}"
.config=${this._tap_action}
.actions=${actions}
.configValue=${"tap_action"}
@value-changed=${this._valueChanged}
></hui-action-editor>
<hui-action-editor
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hold_action"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.hass=${this.hass}
.config="${this._hold_action}"
.actions="${actions}"
.configValue="${"hold_action"}"
@value-changed="${this._valueChanged}"
.config=${this._hold_action}
.actions=${actions}
.configValue=${"hold_action"}
@value-changed=${this._valueChanged}
></hui-action-editor>
<hui-theme-select-editor
.hass=${this.hass}
.value="${this._theme}"
.configValue="${"theme"}"
@value-changed="${this._valueChanged}"
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</div>
</hui-config-element-template>
`;
}

View File

@@ -3,7 +3,8 @@ import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import {
CSSResult,
css,
CSSResultArray,
customElement,
html,
internalProperty,
@@ -13,8 +14,8 @@ import {
} from "lit-element";
import { assert, boolean, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/ha-formfield";
import "../../../../components/ha-settings-row";
import "../../../../components/ha-switch";
import { ActionConfig } from "../../../../data/lovelace";
import { HomeAssistant } from "../../../../types";
@@ -23,6 +24,7 @@ import "../../components/hui-action-editor";
import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { actionConfigStruct, EditorTarget } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -48,6 +50,8 @@ export class HuiPictureEntityCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: PictureEntityCardConfig;
public setConfig(config: PictureEntityCardConfig): void {
@@ -106,148 +110,137 @@ export class HuiPictureEntityCardEditor extends LitElement
const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
const views = ["auto", "live"];
const dir = computeRTLDirection(this.hass!);
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value="${this._entity}"
.configValue=${"entity"}
@value-changed="${this._valueChanged}"
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._name}"
.configValue="${"name"}"
@value-changed="${this._valueChanged}"
></paper-input>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.image"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._image}"
.configValue="${"image"}"
@value-changed="${this._valueChanged}"
></paper-input>
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.camera_image"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.hass=${this.hass}
.value="${this._camera_image}"
.configValue=${"camera_image"}
@value-changed="${this._valueChanged}"
.includeDomains=${includeDomains}
allow-custom-entity
></ha-entity-picker>
<div class="side-by-side">
<paper-dropdown-menu
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
allow-custom-entity
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.camera_view"
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
"ui.panel.lovelace.editor.card.config.required"
)})"
.configValue="${"camera_view"}"
@value-changed="${this._valueChanged}"
>
<paper-listbox
slot="dropdown-content"
.selected="${views.indexOf(this._camera_view)}"
>
${views.map((view) => {
return html` <paper-item>${view}</paper-item> `;
})}
</paper-listbox>
</paper-dropdown-menu>
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
@value-changed=${this._valueChanged}
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._aspect_ratio}"
.configValue="${"aspect_ratio"}"
@value-changed="${this._valueChanged}"
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div class="side-by-side">
<div>
<ha-formfield
.label=${this.hass.localize(
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.image"
)}
.value=${this._image}
.configValue=${"image"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_name"
)}
.dir=${dir}
>
<ha-switch
.checked="${this._config!.show_name !== false}"
.configValue="${"show_name"}"
@change="${this._change}"
></ha-switch
></ha-formfield>
</div>
<div>
<ha-formfield
.label=${this.hass.localize(
</span>
<ha-switch
.checked=${this._config!.show_name !== false}
.configValue=${"show_name"}
@change=${this._change}
></ha-switch>
</ha-settings-row>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.show_state"
)}
.dir=${dir}
>
<ha-switch
.checked="${this._config!.show_state !== false}"
.configValue="${"show_state"}"
@change="${this._change}"
></ha-switch
></ha-formfield>
</div>
</div>
<div class="side-by-side">
</span>
<ha-switch
.checked=${this._config!.show_state !== false}
.configValue=${"show_state"}
@change=${this._change}
></ha-switch>
</ha-settings-row>
<hui-action-editor
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.tap_action"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.hass=${this.hass}
.config="${this._tap_action}"
.actions="${actions}"
.configValue="${"tap_action"}"
@value-changed="${this._valueChanged}"
.config=${this._tap_action}
.actions=${actions}
.configValue=${"tap_action"}
@value-changed=${this._valueChanged}
></hui-action-editor>
<ha-expansion-panel>
<span class="title" slot="title">
${this.hass.localize(
`ui.panel.lovelace.editor.card.picture-entity.camera_options`
)}
</span>
<ha-entity-picker
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.camera_image"
)}
.hass=${this.hass}
.value=${this._camera_image}
.configValue=${"camera_image"}
@value-changed=${this._valueChanged}
.includeDomains=${includeDomains}
allow-custom-entity
></ha-entity-picker>
<paper-dropdown-menu
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.camera_view"
)}
.configValue=${"camera_view"}
@value-changed=${this._valueChanged}
>
<paper-listbox
slot="dropdown-content"
.selected=${views.indexOf(this._camera_view)}
>
${views.map((view) => {
return html`<paper-item>${view}</paper-item>`;
})}
</paper-listbox>
</paper-dropdown-menu>
</ha-expansion-panel>
</div>
<div slot="advanced" class="card-config">
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)}
.value=${this._aspect_ratio}
.configValue=${"aspect_ratio"}
@value-changed=${this._valueChanged}
></paper-input>
<hui-action-editor
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hold_action"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.hass=${this.hass}
.config="${this._hold_action}"
.actions="${actions}"
.configValue="${"hold_action"}"
@value-changed="${this._valueChanged}"
.config=${this._hold_action}
.actions=${actions}
.configValue=${"hold_action"}
@value-changed=${this._valueChanged}
></hui-action-editor>
<hui-theme-select-editor
.hass=${this.hass}
.value="${this._theme}"
.configValue="${"theme"}"
@value-changed="${this._valueChanged}"
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</div>
</hui-config-element-template>
`;
}
@@ -294,8 +287,15 @@ export class HuiPictureEntityCardEditor extends LitElement
fireEvent(this, "config-changed", { config: this._config });
}
static get styles(): CSSResult {
return configElementStyle;
static get styles(): CSSResultArray {
return [
configElementStyle,
css`
paper-dropdown-menu {
width: 100%;
}
`,
];
}
}

View File

@@ -3,7 +3,8 @@ import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import {
CSSResult,
css,
CSSResultArray,
customElement,
html,
internalProperty,
@@ -22,6 +23,7 @@ import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import { EntityConfig } from "../../entity-rows/types";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { processEditorEntities } from "../process-editor-entities";
import {
actionConfigStruct,
@@ -51,6 +53,8 @@ export class HuiPictureGlanceCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: PictureGlanceCardConfig;
@internalProperty() private _configEntities?: EntityConfig[];
@@ -112,120 +116,114 @@ export class HuiPictureGlanceCardEditor extends LitElement
const views = ["auto", "live"];
return html`
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._title}"
.configValue="${"title"}"
@value-changed="${this._valueChanged}"
></paper-input>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.image"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._image}"
.configValue="${"image"}"
@value-changed="${this._valueChanged}"
></paper-input>
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.camera_image"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.hass=${this.hass}
.value="${this._camera_image}"
.configValue=${"camera_image"}
@value-changed="${this._valueChanged}"
allow-custom-entity
.includeDomains=${includeDomains}
></ha-entity-picker>
<div class="side-by-side">
<paper-dropdown-menu
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.camera_view"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.configValue="${"camera_view"}"
@value-changed="${this._valueChanged}"
>
<paper-listbox
slot="dropdown-content"
.selected="${views.indexOf(this._camera_view)}"
>
${views.map((view) => {
return html` <paper-item>${view}</paper-item> `;
})}
</paper-listbox>
</paper-dropdown-menu>
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._aspect_ratio}"
.configValue="${"aspect_ratio"}"
@value-changed="${this._valueChanged}"
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)}
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.image"
)}
.value=${this._image}
.configValue=${"image"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.picture-glance.state_entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.hass=${this.hass}
.value="${this._entity}"
.configValue=${"entity"}
@value-changed="${this._valueChanged}"
allow-custom-entity
></ha-entity-picker>
<div class="side-by-side">
<hui-action-editor
.label="${this.hass.localize(
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.tap_action"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
)}
.hass=${this.hass}
.config="${this._tap_action}"
.actions="${actions}"
.configValue="${"tap_action"}"
@value-changed="${this._valueChanged}"
></hui-action-editor>
<hui-action-editor
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hold_action"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.hass=${this.hass}
.config="${this._hold_action}"
.actions="${actions}"
.configValue="${"hold_action"}"
@value-changed="${this._valueChanged}"
.config=${this._tap_action}
.actions=${actions}
.configValue=${"tap_action"}
@value-changed=${this._valueChanged}
></hui-action-editor>
</div>
<hui-entity-editor
.hass=${this.hass}
.entities="${this._configEntities}"
@entities-changed="${this._valueChanged}"
.entities=${this._configEntities}
@entities-changed=${this._valueChanged}
></hui-entity-editor>
<hui-theme-select-editor
.hass=${this.hass}
.value="${this._theme}"
.configValue="${"theme"}"
@value-changed="${this._valueChanged}"
></hui-theme-select-editor>
</div>
<ha-expansion-panel>
<span class="title" slot="title">
${this.hass.localize(
`ui.panel.lovelace.editor.card.picture-entity.camera_options`
)}
</span>
<ha-entity-picker
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.camera_image"
)}
.hass=${this.hass}
.value=${this._camera_image}
.configValue=${"camera_image"}
@value-changed=${this._valueChanged}
.includeDomains=${includeDomains}
allow-custom-entity
></ha-entity-picker>
<paper-dropdown-menu
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.camera_view"
)}
.configValue=${"camera_view"}
@value-changed=${this._valueChanged}
>
<paper-listbox
slot="dropdown-content"
.selected=${views.indexOf(this._camera_view)}
>
${views.map((view) => {
return html`<paper-item>${view}</paper-item>`;
})}
</paper-listbox>
</paper-dropdown-menu>
</ha-expansion-panel>
<div slot="advanced" class="card-config">
<ha-entity-picker
allow-custom-entity
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)}
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
@value-changed=${this._valueChanged}
></ha-entity-picker>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
)}
.value=${this._aspect_ratio}
.configValue=${"aspect_ratio"}
@value-changed=${this._valueChanged}
></paper-input>
<hui-action-editor
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hold_action"
)}
.hass=${this.hass}
.config=${this._hold_action}
.actions=${actions}
.configValue=${"hold_action"}
@value-changed=${this._valueChanged}
></hui-action-editor>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</hui-config-element-template>
`;
}
@@ -258,8 +256,15 @@ export class HuiPictureGlanceCardEditor extends LitElement
fireEvent(this, "config-changed", { config: this._config });
}
static get styles(): CSSResult {
return configElementStyle;
static get styles(): CSSResultArray {
return [
configElementStyle,
css`
paper-dropdown-menu {
width: 100%;
}
`,
];
}
}

View File

@@ -16,6 +16,7 @@ import { HomeAssistant } from "../../../../types";
import { PlantStatusCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -33,6 +34,8 @@ export class HuiPlantStatusCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: PlantStatusCardConfig;
public setConfig(config: PlantStatusCardConfig): void {
@@ -58,37 +61,42 @@ export class HuiPlantStatusCardEditor extends LitElement
}
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value="${this._entity}"
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change="${this._valueChanged}"
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._name}"
.configValue="${"name"}"
@value-changed="${this._valueChanged}"
></paper-input>
<hui-theme-select-editor
.hass=${this.hass}
.value="${this._theme}"
.configValue="${"theme"}"
@value-changed="${this._valueChanged}"
></hui-theme-select-editor>
</div>
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
allow-custom-entity
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change=${this._valueChanged}
></ha-entity-picker>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div slot="advanced" class="card-config">
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</hui-config-element-template>
`;
}

View File

@@ -17,11 +17,13 @@ import { stateIcon } from "../../../../common/entity/state_icon";
import "../../../../components/entity/ha-entity-picker";
import "../../../../components/ha-formfield";
import "../../../../components/ha-icon-input";
import "../../../../components/ha-settings-row";
import "../../../../components/ha-switch";
import { HomeAssistant } from "../../../../types";
import { SensorCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -44,6 +46,8 @@ export class HuiSensorCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: SensorCardConfig;
public setConfig(config: SensorCardConfig): void {
@@ -91,105 +95,99 @@ export class HuiSensorCardEditor extends LitElement
const graphs = ["line", "none"];
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
<div class="side-by-side">
<ha-icon-input
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.icon"
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
<div class="side-by-side">
<paper-dropdown-menu
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.sensor.graph_type"
)}
.configValue=${"graph"}
@value-changed=${this._valueChanged}
>
<paper-listbox
slot="dropdown-content"
.selected=${graphs.indexOf(this._graph)}
>
${graphs.map((graph) => {
return html`<paper-item>${graph}</paper-item>`;
})}
</paper-listbox>
</paper-dropdown-menu>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hours_to_show"
)}
type="number"
.value=${this._hours_to_show}
.configValue=${"hours_to_show"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
</div>
<div slot="advanced" class="card-config">
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.sensor.show_more_detail"
)}
</span>
<ha-switch
.checked=${this._detail === 2}
.configValue=${"detail"}
@change=${this._change}
></ha-switch>
</ha-settings-row>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.unit"
)}
.value=${this._unit}
.configValue=${"unit"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-icon-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.icon"
)}
.value=${this._icon}
.placeholder=${this._icon ||
stateIcon(this.hass.states[this._entity])}
.configValue=${"icon"}
@value-changed=${this._valueChanged}
></ha-icon-input>
<paper-dropdown-menu
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.sensor.graph_type"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.configValue=${"graph"}
@value-changed=${this._valueChanged}
>
<paper-listbox
slot="dropdown-content"
.selected=${graphs.indexOf(this._graph)}
>
${graphs.map((graph) => {
return html`<paper-item>${graph}</paper-item>`;
})}
</paper-listbox>
</paper-dropdown-menu>
</div>
<div class="side-by-side">
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.unit"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._unit}
.configValue=${"unit"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-formfield
label=${this.hass.localize(
"ui.panel.lovelace.editor.card.sensor.show_more_detail"
)}
>
<ha-switch
.checked=${this._detail === 2}
.configValue=${"detail"}
@change=${this._change}
></ha-switch>
</ha-formfield>
</div>
<div class="side-by-side">
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.hours_to_show"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
type="number"
.value=${this._hours_to_show}
.configValue=${"hours_to_show"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
</div>
</hui-config-element-template>
`;
}

View File

@@ -4,19 +4,20 @@ import {
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
internalProperty,
TemplateResult,
} from "lit-element";
import { assert, object, optional, string } from "superstruct";
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 { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { string, assert, object, optional } from "superstruct";
const cardConfigStruct = object({
type: string(),
@@ -29,6 +30,8 @@ export class HuiShoppingListEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: ShoppingListCardConfig;
public setConfig(config: ShoppingListCardConfig): void {
@@ -50,33 +53,38 @@ export class HuiShoppingListEditor extends LitElement
}
return html`
<div class="card-config">
${!isComponentLoaded(this.hass, "shopping_list")
? html`
<div class="error">
${this.hass.localize(
"ui.panel.lovelace.editor.card.shopping-list.integration_not_loaded"
)}
</div>
`
: ""}
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._title}"
.configValue="${"title"}"
@value-changed="${this._valueChanged}"
></paper-input>
<hui-theme-select-editor
.hass=${this.hass}
.value="${this._theme}"
.configValue="${"theme"}"
@value-changed="${this._valueChanged}"
></hui-theme-select-editor>
</div>
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
${!isComponentLoaded(this.hass, "shopping_list")
? html`
<div class="error">
${this.hass.localize(
"ui.panel.lovelace.editor.card.shopping-list.integration_not_loaded"
)}
</div>
`
: ""}
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.title"
)}
.value=${this._title}
.configValue=${"title"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div slot="advanced" class="card-config">
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</hui-config-element-template>
`;
}

View File

@@ -15,6 +15,7 @@ import { HomeAssistant } from "../../../../types";
import { ThermostatCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -32,6 +33,8 @@ export class HuiThermostatCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: ThermostatCardConfig;
public setConfig(config: ThermostatCardConfig): void {
@@ -57,37 +60,42 @@ export class HuiThermostatCardEditor extends LitElement
}
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value="${this._entity}"
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change="${this._valueChanged}"
allow-custom-entity
></ha-entity-picker>
<paper-input
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value="${this._name}"
.configValue="${"name"}"
@value-changed="${this._valueChanged}"
></paper-input>
<hui-theme-select-editor
.hass=${this.hass}
.value="${this._theme}"
.configValue="${"theme"}"
@value-changed="${this._valueChanged}"
></hui-theme-select-editor>
</div>
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
</div>
<div slot="advanced" class="card-config">
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
</hui-config-element-template>
`;
}

View File

@@ -9,15 +9,16 @@ import {
} from "lit-element";
import { assert, boolean, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/entity/ha-entity-attribute-picker";
import "../../../../components/entity/ha-entity-picker";
import "../../../../components/ha-formfield";
import "../../../../components/ha-settings-row";
import "../../../../components/ha-switch";
import "../../../../components/entity/ha-entity-attribute-picker";
import { HomeAssistant } from "../../../../types";
import { WeatherForecastCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import "../hui-config-element-template";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
@@ -37,6 +38,8 @@ export class HuiWeatherForecastCardEditor extends LitElement
implements LovelaceCardEditor {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public isAdvanced?: boolean;
@internalProperty() private _config?: WeatherForecastCardConfig;
public setConfig(config: WeatherForecastCardConfig): void {
@@ -70,31 +73,56 @@ export class HuiWeatherForecastCardEditor extends LitElement
}
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<div class="side-by-side">
<paper-input
<hui-config-element-template
.hass=${this.hass}
.isAdvanced=${this.isAdvanced}
>
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
.includeDomains=${includeDomains}
@change=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<paper-input
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.name"
)}
.value=${this._name}
.configValue=${"name"}
@value-changed=${this._valueChanged}
></paper-input>
<ha-settings-row three-line>
<span slot="heading">
${this.hass.localize(
"ui.panel.lovelace.editor.card.weather-forecast.show_forecast"
)}
</span>
<ha-switch
.checked=${this._config!.show_forecast !== false}
.configValue=${"show_forecast"}
@change=${this._valueChanged}
></ha-switch>
</ha-settings-row>
</div>
<div slot="advanced" class="card-config">
<ha-entity-attribute-picker
.hass=${this.hass}
.entityId=${this._entity}
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.secondary_info_attribute"
)}
.value=${this._secondary_info_attribute}
.configValue=${"secondary_info_attribute"}
@value-changed=${this._valueChanged}
></ha-entity-attribute-picker>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
@@ -102,33 +130,7 @@ export class HuiWeatherForecastCardEditor extends LitElement
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
<div class="side-by-side">
<ha-entity-attribute-picker
.hass=${this.hass}
.entityId=${this._entity}
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.secondary_info_attribute"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._secondary_info_attribute}
.configValue=${"secondary_info_attribute"}
@value-changed=${this._valueChanged}
></ha-entity-attribute-picker>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.card.weather-forecast.show_forecast"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${this._config!.show_forecast !== false}
.configValue=${"show_forecast"}
@change=${this._valueChanged}
></ha-switch
></ha-formfield>
</div>
</div>
</hui-config-element-template>
`;
}

View File

@@ -53,7 +53,7 @@ export class HuiCreateDialogHeaderFooter extends LitElement
`ui.panel.lovelace.editor.header-footer.choose_header_footer`,
"type",
this.hass!.localize(
`ui.panel.lovelace.editor.header-footer.${this._params.type}`
`ui.panel.lovelace.editor.header-footer.${this._params.type}.name`
)
)
)}

View File

@@ -1,5 +1,5 @@
import "@material/mwc-icon-button/mwc-icon-button";
import { mdiClose, mdiPencil, mdiPlus } from "@mdi/js";
import { mdiChevronRight } from "@mdi/js";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import {
@@ -31,56 +31,26 @@ export class HuiHeaderFooterEditor extends LitElement {
protected render(): TemplateResult {
return html`
<div>
<div @click=${this._click}>
<span>
${this.hass.localize(
`ui.panel.lovelace.editor.header-footer.${this.configValue}`
)}:
${!this.config?.type
? this.hass!.localize("ui.panel.lovelace.editor.common.none")
: this.hass!.localize(
`ui.panel.lovelace.editor.header-footer.types.${this.config?.type}.name`
)}
`ui.panel.lovelace.editor.header-footer.${this.configValue}.manage`
)}
</span>
</div>
<div>
${!this.config?.type
? html`
<mwc-icon-button
aria-label=${this.hass!.localize(
"ui.panel.lovelace.editor.common.add"
)}
class="add-icon"
@click=${this._add}
>
<ha-svg-icon .path=${mdiPlus}></ha-svg-icon>
</mwc-icon-button>
`
: html`
<mwc-icon-button
aria-label=${this.hass!.localize(
"ui.panel.lovelace.editor.common.clear"
)}
class="remove-icon"
@click=${this._delete}
>
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button>
<mwc-icon-button
aria-label=${this.hass!.localize(
"ui.panel.lovelace.editor.common.edit"
)}
class="edit-icon"
@click=${this._edit}
>
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
</mwc-icon-button>
`}
<ha-svg-icon .path=${mdiChevronRight}></ha-svg-icon>
</div>
`;
}
private _edit(): void {
private _click(): void {
if (!this.config?.type) {
showCreateHeaderFooterDialog(this, {
pickHeaderFooter: (config) => this._elementPicked(config),
type: this.configValue,
});
return;
}
fireEvent(this, "edit-detail-element", {
subElementConfig: {
elementConfig: this.config,
@@ -89,13 +59,6 @@ export class HuiHeaderFooterEditor extends LitElement {
});
}
private _add(): void {
showCreateHeaderFooterDialog(this, {
pickHeaderFooter: (config) => this._elementPicked(config),
type: this.configValue,
});
}
private _elementPicked(config: LovelaceHeaderFooterConfig): void {
fireEvent(this, "value-changed", { value: config });
fireEvent(this, "edit-detail-element", {
@@ -106,35 +69,20 @@ export class HuiHeaderFooterEditor extends LitElement {
});
}
private _delete(): void {
fireEvent(this, "value-changed", { value: "" });
}
static get styles(): CSSResult {
return css`
:host {
font-size: 16px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
padding-bottom: 12px;
}
:host > div {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 12px;
min-height: 48px;
cursor: pointer;
}
mwc-icon-button,
.header-footer-icon {
--mdc-icon-button-size: 36px;
ha-svg-icon {
color: var(--secondary-text-color);
}
.header-footer-icon {
padding-right: 8px;
}
`;
}
}

View File

@@ -0,0 +1,46 @@
import {
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../components/ha-expansion-panel";
import { HomeAssistant } from "../../../types";
import { configElementStyle } from "./config-elements/config-elements-style";
@customElement("hui-config-element-template")
export class HuiConfigElementTemplate extends LitElement {
public hass!: HomeAssistant;
@property({ type: Boolean }) public isAdvanced? = false;
protected render(): TemplateResult {
return html`
<slot></slot>
${this.isAdvanced
? html`
<ha-expansion-panel>
<span class="title" slot="title">
${this.hass.localize(
`ui.panel.lovelace.editor.common.advanced_options`
)}
</span>
<slot name="advanced"></slot>
</ha-expansion-panel>
`
: ""}
`;
}
static get styles(): CSSResult {
return configElementStyle;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-config-element-template": HuiConfigElementTemplate;
}
}

View File

@@ -31,7 +31,11 @@ import { GUISupportError } from "./gui-support-error";
import { EditSubElementEvent, GUIModeChangedEvent } from "./types";
export interface ConfigChangedEvent {
config: LovelaceCardConfig | LovelaceRowConfig | LovelaceHeaderFooterConfig;
config:
| LovelaceCardConfig
| LovelaceRowConfig
| LovelaceHeaderFooterConfig
| undefined;
error?: string;
guiModeAvailable?: boolean;
}
@@ -55,6 +59,8 @@ export abstract class HuiElementEditor<T> extends LitElement {
@property({ attribute: false }) public lovelace?: LovelaceConfig;
@property({ attribute: false }) public isAdvanced?: boolean;
@internalProperty() private _yaml?: string;
@internalProperty() private _config?: T;
@@ -144,10 +150,14 @@ export abstract class HuiElementEditor<T> extends LitElement {
});
}
public toggleMode() {
public toggleMode(): void {
this.GUImode = !this.GUImode;
}
public remove(): void {
this._configElement?.remove();
}
public refreshYamlEditor(focus = false) {
if (this._configElement?.refreshYamlEditor) {
this._configElement.refreshYamlEditor(focus);
@@ -237,6 +247,9 @@ export abstract class HuiElementEditor<T> extends LitElement {
) {
this._configElement.lovelace = this.lovelace;
}
if (this._configElement && changedProperties.has("isAdvanced")) {
this._configElement.isAdvanced = this.isAdvanced;
}
}
private _handleUIConfigChanged(ev: UIConfigChangedEvent) {
@@ -283,6 +296,7 @@ export abstract class HuiElementEditor<T> extends LitElement {
}
configElement.hass = this.hass;
configElement.isAdvanced = this.isAdvanced;
if ("lovelace" in configElement) {
configElement.lovelace = this.lovelace;
}

View File

@@ -1,5 +1,6 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-icon-button";
import { mdiClose, mdiDrag, mdiPencil } from "@mdi/js";
import { mdiChevronRight, mdiDragVerticalVariant } from "@mdi/js";
import {
css,
CSSResult,
@@ -19,7 +20,7 @@ import Sortable, {
} from "sortablejs/modular/sortable.core.esm";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/entity/ha-entity-picker";
import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker";
import "../../../components/ha-card";
import "../../../components/ha-svg-icon";
import { sortableStyles } from "../../../resources/ha-sortable-style";
import { HomeAssistant } from "../../../types";
@@ -63,78 +64,72 @@ export class HuiEntitiesCardRowEditor extends LitElement {
}
return html`
<h3>
${this.label ||
`${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entities"
)} (${this.hass!.localize(
"ui.panel.lovelace.editor.card.config.required"
)})`}
</h3>
<div class="title">
<span>
${this.label ||
`${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.entities"
)}:`}
</span>
<mwc-button
.label=${this.hass!.localize(
"ui.panel.lovelace.editor.card.entities.add_row"
)}
@click=${this._addEntity}
></mwc-button>
</div>
<div class="entities">
${guard([this.entities, this._renderEmptySortable], () =>
this._renderEmptySortable
? ""
: this.entities!.map((entityConf, index) => {
const stateObj = this.hass!.states[
(entityConf as EntityConfig).entity
];
return html`
<div class="entity">
<ha-svg-icon class="handle" .path=${mdiDrag}></ha-svg-icon>
${entityConf.type
? html`
<div class="special-row">
<div>
<span>
${this.hass!.localize(
`ui.panel.lovelace.editor.card.entities.entity_row.${entityConf.type}`
)}
</span>
<span class="secondary"
>${this.hass!.localize(
"ui.panel.lovelace.editor.card.entities.edit_special_row"
)}</span
>
</div>
</div>
`
: html`
<ha-entity-picker
allow-custom-entity
hideClearIcon
.hass=${this.hass}
.value=${(entityConf as EntityConfig).entity}
.index=${index}
@value-changed=${this._valueChanged}
></ha-entity-picker>
`}
<mwc-icon-button
aria-label=${this.hass!.localize(
"ui.components.entity.entity-picker.clear"
)}
class="remove-icon"
.index=${index}
@click=${this._removeRow}
>
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button>
<mwc-icon-button
aria-label=${this.hass!.localize(
"ui.components.entity.entity-picker.edit"
)}
<ha-card
outlined
class="entity"
.index=${index}
@click=${this._editRow}
>
<ha-svg-icon
class="handle"
.path=${mdiDragVerticalVariant}
></ha-svg-icon>
<div class="info">
<span class="primary">
${entityConf.type
? html`
${this.hass!.localize(
`ui.panel.lovelace.editor.card.entities.entity_row.${entityConf.type}`
)}
`
: html`
${(entityConf as EntityConfig).name ||
stateObj?.attributes.friendly_name ||
(entityConf as EntityConfig).entity}
`}
</span>
<span class="secondary">
${entityConf.type
? html`
${this.hass!.localize(
"ui.panel.lovelace.editor.card.entities.edit_special_row"
)}
`
: html`${(entityConf as EntityConfig).entity}`}
</span>
</div>
<ha-svg-icon
class="edit-icon"
.index=${index}
@click=${this._editRow}
>
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
</mwc-icon-button>
</div>
.path=${mdiChevronRight}
></ha-svg-icon>
</ha-card>
`;
})
)}
</div>
<ha-entity-picker
.hass=${this.hass}
@value-changed=${this._addEntity}
></ha-entity-picker>
`;
}
@@ -189,16 +184,20 @@ export class HuiEntitiesCardRowEditor extends LitElement {
});
}
private async _addEntity(ev: CustomEvent): Promise<void> {
const value = ev.detail.value;
if (value === "") {
return;
}
private async _addEntity(): Promise<void> {
const newConfigEntities = this.entities!.concat({
entity: value as string,
entity: Object.keys(this.hass!.states)[0],
});
(ev.target as HaEntityPicker).value = "";
fireEvent(this, "entities-changed", { entities: newConfigEntities });
const index = newConfigEntities.length - 1;
fireEvent(this, "edit-detail-element", {
subElementConfig: {
index,
type: "row",
elementConfig: newConfigEntities[index],
},
});
}
private _rowMoved(ev: SortableEvent): void {
@@ -213,32 +212,6 @@ export class HuiEntitiesCardRowEditor extends LitElement {
fireEvent(this, "entities-changed", { entities: newEntities });
}
private _removeRow(ev: CustomEvent): void {
const index = (ev.currentTarget as any).index;
const newConfigEntities = this.entities!.concat();
newConfigEntities.splice(index, 1);
fireEvent(this, "entities-changed", { entities: newConfigEntities });
}
private _valueChanged(ev: CustomEvent): void {
const value = ev.detail.value;
const index = (ev.target as any).index;
const newConfigEntities = this.entities!.concat();
if (value === "") {
newConfigEntities.splice(index, 1);
} else {
newConfigEntities[index] = {
...newConfigEntities[index],
entity: value!,
};
}
fireEvent(this, "entities-changed", { entities: newConfigEntities });
}
private _editRow(ev: CustomEvent): void {
const index = (ev.currentTarget as any).index;
fireEvent(this, "edit-detail-element", {
@@ -254,43 +227,62 @@ export class HuiEntitiesCardRowEditor extends LitElement {
return [
sortableStyles,
css`
.entity {
.title {
font-size: 18px;
display: flex;
justify-content: space-between;
align-items: center;
}
.entity .handle {
padding-right: 8px;
cursor: move;
.entity {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px;
margin: 8px 0;
cursor: pointer;
}
.entity ha-entity-picker {
flex-grow: 1;
.handle {
min-width: 18px;
--mdc-icon-size: 18px;
padding-right: 8px;
cursor: move;
color: var(--secondary-text-color);
}
.special-row {
height: 60px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: space-between;
flex-grow: 1;
}
.special-row div {
.info {
display: flex;
flex-direction: column;
overflow: hidden;
text-overflow: ellipsis;
overflow: hidden;
flex-grow: 1;
}
.remove-icon,
.edit-icon {
--mdc-icon-button-size: 36px;
min-width: 24px;
color: var(--secondary-text-color);
}
.primary {
text-overflow: ellipsis;
overflow: hidden;
}
.secondary {
font-size: 12px;
color: var(--secondary-text-color);
text-overflow: ellipsis;
overflow: hidden;
}
`,
];

View File

@@ -1,6 +1,14 @@
import "@material/mwc-button";
import "@material/mwc-icon-button";
import { mdiArrowLeft } from "@mdi/js";
import "@material/mwc-list/mwc-list-item";
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
import {
mdiArrowLeft,
mdiCodeBracesBox,
mdiDotsVertical,
mdiFormSelect,
mdiTrashCanOutline,
} from "@mdi/js";
import {
css,
CSSResult,
@@ -13,6 +21,8 @@ import {
TemplateResult,
} from "lit-element";
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
import "../../../components/ha-button-menu";
import "../../../components/ha-svg-icon";
import type { HomeAssistant } from "../../../types";
import type { LovelaceRowConfig } from "../entity-rows/types";
@@ -34,6 +44,8 @@ export class HuiSubElementEditor extends LitElement {
@property({ attribute: false }) public config!: SubElementEditorConfig;
@property({ type: Boolean }) public isAdvancedUser? = false;
@internalProperty() private _guiModeAvailable = true;
@internalProperty() private _guiMode = true;
@@ -55,17 +67,64 @@ export class HuiSubElementEditor extends LitElement {
)}</span
>
</div>
<mwc-button
<ha-button-menu
fixed
corner="BOTTOM_START"
slot="secondaryAction"
.disabled=${!this._guiModeAvailable}
@click=${this._toggleMode}
@closed=${(ev) => ev.stopPropagation()}
>
${this.hass.localize(
this._guiMode
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
)}
</mwc-button>
<mwc-icon-button
slot="trigger"
.label=${this.hass!.localize("ui.panel.lovelace.editor.menu.open")}
.title=${this.hass!.localize("ui.panel.lovelace.editor.menu.open")}
>
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
${this.isAdvancedUser
? html`
<mwc-list-item
graphic="icon"
.label=${this.hass!.localize(
this._guiMode
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
)}
.disabled=${!this._guiModeAvailable}
@request-selected=${this._toggleMode}
>
<span
>${this.hass!.localize(
this._guiMode
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
)}</span
>
<ha-svg-icon
slot="graphic"
.path=${this._guiMode ? mdiCodeBracesBox : mdiFormSelect}
></ha-svg-icon>
</mwc-list-item>
`
: ""}
<mwc-list-item
graphic="icon"
.label=${this.hass!.localize(
"ui.panel.lovelace.editor.common.delete"
)}
@request-selected=${this._remove}
>
<span
>${this.hass!.localize(
"ui.panel.lovelace.editor.common.delete"
)}</span
>
<ha-svg-icon
slot="graphic"
.path=${mdiTrashCanOutline}
></ha-svg-icon>
</mwc-list-item>
</ha-button-menu>
</div>
${this.config.type === "row"
? html`
@@ -95,10 +154,20 @@ export class HuiSubElementEditor extends LitElement {
fireEvent(this, "go-back");
}
private _toggleMode(): void {
private _toggleMode(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
this._editorElement?.toggleMode();
}
private _remove(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
fireEvent(this, "config-changed", { config: undefined });
}
private _handleGUIModeChanged(ev: HASSDomEvent<GUIModeChangedEvent>): void {
ev.stopPropagation();
this._guiMode = ev.detail.guiMode;

View File

@@ -793,6 +793,10 @@ class HUIRoot extends LitElement {
ha-app-layout {
min-height: 100%;
background: var(
--lovelace-background,
var(--primary-background-color)
);
}
ha-tabs {
width: 100%;
@@ -880,12 +884,6 @@ class HUIRoot extends LitElement {
.menu-link {
text-decoration: none;
}
hui-view {
background: var(
--lovelace-background,
var(--primary-background-color)
);
}
`,
];
}

View File

@@ -85,6 +85,7 @@ export interface LovelaceRowEditor extends LovelaceGenericElementEditor {
export interface LovelaceGenericElementEditor extends HTMLElement {
hass?: HomeAssistant;
lovelace?: LovelaceConfig;
isAdvanced?: boolean;
setConfig(config: any): void;
refreshYamlEditor?: (focus: boolean) => void;
}

View File

@@ -293,7 +293,7 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
.column > * {
display: block;
margin: var(--masonry-view-card-margin, 4px 4px 8px);
margin: 4px 4px 8px;
}
ha-fab {

View File

@@ -0,0 +1,154 @@
import "@material/mwc-button";
import "@polymer/paper-dialog/paper-dialog";
import "../../components/ha-circular-progress";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../components/ha-card";
import LocalizeMixin from "../../mixins/localize-mixin";
import "../../styles/polymer-ha-style";
/*
* @appliesMixin LocalizeMixin
*/
class HaChangePasswordCard extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style include="ha-style">
.error {
color: red;
}
.status {
color: var(--primary-color);
}
.error,
.status {
position: absolute;
top: -4px;
}
.currentPassword {
margin-top: -4px;
}
</style>
<div>
<ha-card
header="[[localize('ui.panel.profile.change_password.header')]]"
>
<div class="card-content">
<template is="dom-if" if="[[_errorMsg]]">
<div class="error">[[_errorMsg]]</div>
</template>
<template is="dom-if" if="[[_statusMsg]]">
<div class="status">[[_statusMsg]]</div>
</template>
<paper-input
class="currentPassword"
label="[[localize('ui.panel.profile.change_password.current_password')]]"
type="password"
value="{{_currentPassword}}"
required
auto-validate
error-message="[[localize('ui.panel.profile.change_password.error_required')]]"
></paper-input>
<template is="dom-if" if="[[_currentPassword]]">
<paper-input
label="[[localize('ui.panel.profile.change_password.new_password')]]"
type="password"
value="{{_password1}}"
required
auto-validate
error-message="[[localize('ui.panel.profile.change_password.error_required')]]"
></paper-input>
<paper-input
label="[[localize('ui.panel.profile.change_password.confirm_new_password')]]"
type="password"
value="{{_password2}}"
required
auto-validate
error-message="[[localize('ui.panel.profile.change_password.error_required')]]"
></paper-input>
</template>
</div>
<div class="card-actions">
<template is="dom-if" if="[[_loading]]">
<div><ha-circular-progress active></ha-circular-progress></div>
</template>
<template is="dom-if" if="[[!_loading]]">
<mwc-button on-click="_changePassword"
>[[localize('ui.panel.profile.change_password.submit')]]</mwc-button
>
</template>
</div>
</ha-card>
</div>
`;
}
static get properties() {
return {
hass: Object,
_loading: {
type: Boolean,
value: false,
},
// Error message when can't talk to server etc
_statusMsg: String,
_errorMsg: String,
_currentPassword: String,
_password1: String,
_password2: String,
};
}
ready() {
super.ready();
this.addEventListener("keypress", (ev) => {
this._statusMsg = null;
if (ev.keyCode === 13) {
this._changePassword();
}
});
}
async _changePassword() {
this._statusMsg = null;
if (!this._currentPassword || !this._password1 || !this._password2) return;
if (this._password1 !== this._password2) {
this._errorMsg = "New password confirmation doesn't match";
return;
}
if (this._currentPassword === this._password1) {
this._errorMsg = "New password must be different than current password";
return;
}
this._loading = true;
this._errorMsg = null;
try {
await this.hass.callWS({
type: "config/auth_provider/homeassistant/change_password",
current_password: this._currentPassword,
new_password: this._password1,
});
this.setProperties({
_statusMsg: "Password changed successfully",
_currentPassword: null,
_password1: null,
_password2: null,
});
} catch (err) {
this._errorMsg = err.message;
}
this._loading = false;
}
}
customElements.define("ha-change-password-card", HaChangePasswordCard);

View File

@@ -1,195 +0,0 @@
import "@polymer/paper-input/paper-input";
import "@polymer/paper-dialog/paper-dialog";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit-element";
import "@material/mwc-button";
import "../../components/ha-circular-progress";
import "../../components/ha-card";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
@customElement("ha-change-password-card")
class HaChangePasswordCard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@internalProperty() private _loading = false;
@internalProperty() private _statusMsg?: string;
@internalProperty() private _errorMsg?: string;
@internalProperty() private _currentPassword?: string;
@internalProperty() private _password?: string;
@internalProperty() private _passwordConfirm?: string;
protected render(): TemplateResult {
return html`
<div>
<ha-card
.header=${this.hass.localize(
"ui.panel.profile.change_password.header"
)}
>
<div class="card-content">
${this._errorMsg
? html` <div class="error">${this._errorMsg}</div> `
: ""}
${this._statusMsg
? html` <div class="status">${this._statusMsg}</div> `
: ""}
<paper-input
id="currentPassword"
.label=${this.hass.localize(
"ui.panel.profile.change_password.current_password"
)}
type="password"
.value=${this._currentPassword}
@value-changed=${this._currentPasswordChanged}
required
></paper-input>
${this._currentPassword
? html` <paper-input
.label=${this.hass.localize(
"ui.panel.profile.change_password.new_password"
)}
name="password"
type="password"
.value=${this._password}
@value-changed=${this._newPasswordChanged}
required
auto-validate
></paper-input>
<paper-input
.label=${this.hass.localize(
"ui.panel.profile.change_password.confirm_new_password"
)}
name="passwordConfirm"
type="password"
.value=${this._passwordConfirm}
@value-changed=${this._newPasswordConfirmChanged}
required
auto-validate
></paper-input>`
: ""}
</div>
<div class="card-actions">
${this._loading
? html`<div>
<ha-circular-progress active></ha-circular-progress>
</div>`
: html`<mwc-button
@click=${this._changePassword}
.disabled=${!this._passwordConfirm}
>${this.hass.localize(
"ui.panel.profile.change_password.submit"
)}</mwc-button
>`}
</div>
</ha-card>
</div>
`;
}
private _currentPasswordChanged(ev: CustomEvent) {
this._currentPassword = ev.detail.value;
}
private _newPasswordChanged(ev: CustomEvent) {
this._password = ev.detail.value;
}
private _newPasswordConfirmChanged(ev: CustomEvent) {
this._passwordConfirm = ev.detail.value;
}
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
this.addEventListener("keypress", (ev) => {
this._statusMsg = undefined;
if (ev.keyCode === 13) {
this._changePassword();
}
});
}
private async _changePassword() {
this._statusMsg = undefined;
if (!this._currentPassword || !this._password || !this._passwordConfirm) {
return;
}
if (this._password !== this._passwordConfirm) {
this._errorMsg = this.hass.localize(
"ui.panel.profile.change_password.error_new_mismatch"
);
return;
}
if (this._currentPassword === this._password) {
this._errorMsg = this.hass.localize(
"ui.panel.profile.change_password.error_new_is_old"
);
return;
}
this._loading = true;
this._errorMsg = undefined;
try {
await this.hass.callWS({
type: "config/auth_provider/homeassistant/change_password",
current_password: this._currentPassword,
new_password: this._password,
});
} catch (err) {
this._errorMsg = err.message;
return;
} finally {
this._loading = false;
}
this._statusMsg = this.hass.localize(
"ui.panel.profile.change_password.success"
);
this._currentPassword = undefined;
this._password = undefined;
this._passwordConfirm = undefined;
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
.error {
color: var(--error-color);
}
.status {
color: var(--primary-color);
}
#currentPassword {
margin-top: -8px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-change-password-card": HaChangePasswordCard;
}
}

View File

@@ -25,7 +25,7 @@ class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
>[[localize('ui.panel.profile.push_notifications.header')]]</span
>
<span slot="description">
[[localize(_descrLocalizeKey)]]
[[_description(_platformLoaded, _pushSupported)]]
<a
href="[[_computeDocumentationUrl(hass)]]"
target="_blank"
@@ -45,10 +45,6 @@ class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
return {
hass: Object,
narrow: Boolean,
_descrLocalizeKey: {
type: String,
computed: "_descriptionKey(_platformLoaded, _pushSupported)",
},
_platformLoaded: {
type: Boolean,
computed: "_compPlatformLoaded(hass)",
@@ -76,7 +72,7 @@ class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
return !platformLoaded || !pushSupported_;
}
_descriptionKey(platformLoaded, pushSupported_) {
_description(platformLoaded, pushSupported_) {
let key;
if (!pushSupported_) {
key = "error_use_https";
@@ -85,7 +81,7 @@ class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
} else {
key = "description";
}
return `ui.panel.profile.push_notifications.${key}`;
return this.localize(`ui.panel.profile.push_notifications.${key}`);
}
}

View File

@@ -98,8 +98,7 @@
"disabled_by": {
"user": "User",
"integration": "Integration",
"config_entry": "Config Entry",
"device": "Device"
"config_entry": "Config Entry"
}
},
"ui": {
@@ -335,16 +334,6 @@
"show_attributes": "Show attributes"
}
},
"target-picker": {
"expand_area_id": "Expand this area in the seperate devices and entities that it contains. After expanding it will not update the devices and entities when the area changes.",
"expand_device_id": "Expand this device in seperate entities. After expanding it will not update the entities when the device changes.",
"remove_area_id": "Remove area",
"remove_device_id": "Remove device",
"remove_entity_id": "Remove entity",
"add_area_id": "Pick area",
"add_device_id": "Pick device",
"add_entity_id": "Pick entity"
},
"user-picker": {
"no_user": "No user",
"add_user": "Add user",
@@ -623,8 +612,6 @@
"unavailable": "This entity is not currently available.",
"enabled_label": "Enable entity",
"enabled_cause": "Disabled by {cause}.",
"device_disabled": "The device of this entity is disabled.",
"open_device_settings": "Open device settings",
"enabled_description": "Disabled entities will not be added to Home Assistant.",
"enabled_delay_confirm": "The enabled entities will be added to Home Assistant in {delay} seconds",
"enabled_restart_confirm": "Restart Home Assistant to finish enabling the entities",
@@ -800,7 +787,7 @@
},
"areas": {
"caption": "Areas",
"description": "Group devices into areas",
"description": "Manage areas in your home",
"data_table": {
"area": "Area",
"devices": "Devices"
@@ -830,7 +817,7 @@
},
"tags": {
"caption": "Tags",
"description": "Trigger automations when a NFC tag, QR code, etc. is scanned",
"description": "Manage tags",
"learn_more": "Learn more about tags",
"no_tags": "No tags",
"add_tag": "Add tag",
@@ -861,7 +848,7 @@
},
"helpers": {
"caption": "Helpers",
"description": "Elements that help build automations",
"description": "Manage elements that help build automations",
"types": {
"input_text": "Text",
"input_number": "Number",
@@ -889,7 +876,7 @@
},
"core": {
"caption": "General",
"description": "Unit system, location, time zone & other general parameters",
"description": "Change your general Home Assistant configuration",
"section": {
"core": {
"header": "General Configuration",
@@ -918,7 +905,7 @@
"caption": "Info",
"copy_raw": "Raw Text",
"copy_github": "For GitHub",
"description": "Version, system health and links to documentation",
"description": "View info about your Home Assistant installation",
"home_assistant_logo": "Home Assistant logo",
"path_configuration": "Path to configuration.yaml: {path}",
"developed_by": "Developed by a bunch of awesome people.",
@@ -953,7 +940,7 @@
},
"lovelace": {
"caption": "Lovelace Dashboards",
"description": "Create customized sets of cards to control your home",
"description": "Manage your Lovelace Dashboards",
"dashboards": {
"default_dashboard": "This is the default dashboard",
"caption": "Dashboards",
@@ -1108,7 +1095,7 @@
},
"automation": {
"caption": "Automations",
"description": "Create custom behavior rules for your home",
"description": "Manage automations",
"picker": {
"header": "Automation Editor",
"introduction": "The automation editor allows you to create and edit automations. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.",
@@ -1488,7 +1475,7 @@
},
"script": {
"caption": "Scripts",
"description": "Execute a sequence of actions",
"description": "Manage scripts",
"picker": {
"header": "Script Editor",
"introduction": "The script editor allows you to create and edit scripts. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.",
@@ -1538,7 +1525,7 @@
},
"scene": {
"caption": "Scenes",
"description": "Capture device states and easily recall them later",
"description": "Manage scenes",
"activated": "Activated scene {name}.",
"picker": {
"header": "Scene Editor",
@@ -1584,7 +1571,7 @@
"cloud": {
"description_login": "Logged in as {email}",
"description_not_login": "Not logged in",
"description_features": "Control home when away and integrate with Alexa and Google Assistant",
"description_features": "Control away from home, integrate with Alexa and Google Assistant.",
"login": {
"title": "Cloud Login",
"introduction": "Home Assistant Cloud provides you with a secure remote connection to your instance while away from home. It also allows you to connect with cloud-only services: Amazon Alexa and Google Assistant.",
@@ -1751,27 +1738,18 @@
"devices": {
"add_prompt": "No {name} have been added using this device yet. You can add one by clicking the + button above.",
"caption": "Devices",
"description": "Manage configured devices",
"description": "Manage connected devices",
"device_info": "Device info",
"unnamed_device": "Unnamed device",
"unknown_error": "Unknown error",
"name": "Name",
"update": "Update",
"no_devices": "No devices",
"enabled_label": "Enable device",
"enabled_cause": "The device is disabled by {cause}.",
"disabled_by": {
"user": "User",
"integration": "Integration",
"config_entry": "Config Entry"
},
"enabled_description": "Disabled devices will not be shown and entities belonging to the device will be disabled and not added to Home Assistant.",
"automation": {
"automations": "Automations",
"no_automations": "No automations",
"unknown_automation": "Unknown automation",
"create": "Create automation with device",
"create_disable": "Can't create automation with disabled device",
"triggers": {
"caption": "Do something when...",
"no_triggers": "No triggers",
@@ -1792,14 +1770,12 @@
"script": {
"scripts": "Scripts",
"no_scripts": "No scripts",
"create": "Create script with device",
"create_disable": "Can't create script with disabled device"
"create": "Create script with device"
},
"scene": {
"scenes": "Scenes",
"no_scenes": "No scenes",
"create": "Create scene with device",
"create_disable": "Can't create scene with disabled device"
"create": "Create scene with device"
},
"cant_edit": "You can only edit items that are created in the UI.",
"device_not_found": "Device not found.",
@@ -1814,7 +1790,6 @@
"scenes": "Scenes",
"confirm_rename_entity_ids": "Do you also want to rename the entity IDs of your entities?",
"confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, dashboards) that is currently using these entities! You will have to update them yourself to use the new entity IDs!",
"disabled": "Disabled",
"data_table": {
"device": "Device",
"manufacturer": "Manufacturer",
@@ -1826,16 +1801,7 @@
"no_area": "No area"
},
"delete": "Delete",
"confirm_delete": "Are you sure you want to delete this device?",
"picker": {
"search": "Search devices",
"filter": {
"filter": "Filter",
"show_disabled": "Show disabled devices",
"hidden_devices": "{number} hidden {number, plural,\n one {device}\n other {devices}\n}",
"show_all": "Show all"
}
}
"confirm_delete": "Are you sure you want to delete this device?"
},
"entities": {
"caption": "Entities",
@@ -1950,7 +1916,7 @@
},
"integrations": {
"caption": "Integrations",
"description": "Manage integrations with services, devices, ...",
"description": "Manage integrations",
"integration": "integration",
"discovered": "Discovered",
"attention": "Attention required",
@@ -2056,8 +2022,7 @@
"system_generated_users_not_removable": "Unable to remove system generated users.",
"system_generated_users_not_editable": "Unable to update system generated users.",
"unnamed_user": "Unnamed User",
"confirm_user_deletion": "Are you sure you want to delete {name}?",
"active_tooltip": "Controls if user can login"
"confirm_user_deletion": "Are you sure you want to delete {name}?"
},
"add_user": {
"caption": "Add user",
@@ -2459,7 +2424,8 @@
"edit": "Edit",
"clear": "Clear",
"add": "Add",
"none": "None"
"delete": "Delete",
"advanced_options": "Advanced Options"
},
"raw_editor": {
"header": "Edit Configuration",
@@ -2599,12 +2565,14 @@
},
"entities": {
"name": "Entities",
"show_header_toggle": "Show Header Toggle?",
"show_header_toggle": "Show/Hide a toggle in the header to control all entities in the card",
"show_header_toggle_secondary": "Only visible when 2 or more entities can be toggled",
"toggle": "Toggle entities.",
"description": "The Entities card is the most common type of card. It groups items together into lists.",
"special_row": "special row",
"edit_special_row": "View the details of this row by clicking the edit button",
"entity_row_editor": "Entity Row Editor",
"add_row": "Add Row",
"secondary_info_values": {
"none": "No Secondary Info",
"entity-id": "Entity ID",
@@ -2702,9 +2670,9 @@
"minimum": "Minimum",
"name": "Name",
"refresh_interval": "Refresh Interval",
"show_icon": "Show Icon?",
"show_name": "Show Name?",
"show_state": "Show State?",
"show_icon": "Show/Hide the icon",
"show_name": "Show/Hide the name of the entity",
"show_state": "Show/Hide the state of the entity",
"tap_action": "Tap Action",
"title": "Title",
"theme": "Theme",
@@ -2714,12 +2682,12 @@
"state": "State",
"secondary_info_attribute": "Secondary Info Attribute",
"search": "Search",
"state_color": "Color icons based on state?"
"state_color": "Color the icons based on their current states"
},
"map": {
"name": "Map",
"geo_location_sources": "Geolocation Sources",
"dark_mode": "Dark Mode?",
"dark_mode": "Change the map colors to a dark theme",
"default_zoom": "Default Zoom",
"hours_to_show": "Hours to Show",
"source": "Source",
@@ -2744,7 +2712,8 @@
},
"picture-entity": {
"name": "Picture Entity",
"description": "The Picture Entity card displays an entity in the form of an image. Instead of images from URL, it can also show the picture of camera entities."
"description": "The Picture Entity card displays an entity in the form of an image. Instead of images from URL, it can also show the picture of camera entities.",
"camera_options": "Camera Options"
},
"picture-glance": {
"name": "Picture Glance",
@@ -2757,7 +2726,7 @@
},
"sensor": {
"name": "Sensor",
"show_more_detail": "Show more detail",
"show_more_detail": "Show/Hide more detail in the line graph",
"graph_type": "Graph Type",
"description": "The Sensor card gives you a quick overview of your sensors state with an optional graph to visualize change over time."
},
@@ -2777,7 +2746,7 @@
"weather-forecast": {
"name": "Weather Forecast",
"description": "The Weather Forecast card displays the weather. Very useful to include on interfaces that people display on the wall.",
"show_forecast": "Show Forecast"
"show_forecast": "Show/Hide the weather forecast at the bottom of the card"
}
},
"view": {
@@ -2796,8 +2765,14 @@
"by_card": "By Card"
},
"header-footer": {
"header": "Header",
"footer": "Footer",
"header": {
"name": "Header",
"manage": "Manage header configuration"
},
"footer": {
"name": "Footer",
"manage": "Manage footer configuration"
},
"choose_header_footer": "Choose a {type}",
"types": {
"graph": {
@@ -2906,10 +2881,7 @@
"new_password": "New Password",
"confirm_new_password": "Confirm New Password",
"error_required": "Required",
"submit": "Submit",
"error_new_mismatch": "Entered new password values do not match",
"error_new_is_old": "New password must be different than current password",
"success": "Password changed successfully"
"submit": "Submit"
},
"mfa": {
"header": "Multi-factor Authentication Modules",

View File

@@ -1406,8 +1406,7 @@
"file_name": "Název souboru",
"name": "Jméno"
},
"learn_more": "Další informace o šablonkách konfigurace",
"use_blueprint": "Vytvořit automatizaci"
"learn_more": "Další informace o šablonkách konfigurace"
}
},
"cloud": {

View File

@@ -1744,7 +1744,6 @@
},
"header": "实体",
"headers": {
"area": "区域",
"entity_id": "实体 ID",
"integration": "集成",
"name": "名称",
@@ -2472,16 +2471,6 @@
"value": "值"
},
"description": "Zigbee 智能家居(ZHA) 网络管理",
"device_pairing_card": {
"CONFIGURED": "配置完成",
"CONFIGURED_status_text": "正在初始化",
"INITIALIZED": "初始化完成",
"INITIALIZED_status_text": "设备可以使用了",
"INTERVIEW_COMPLETE": "协商完成",
"INTERVIEW_COMPLETE_status_text": "正在配置",
"PAIRED": "发现设备",
"PAIRED_status_text": "开始协商"
},
"devices": {
"header": "Zigbee 家庭自动化 - 设备"
},