mirror of
				https://github.com/home-assistant/frontend.git
				synced 2025-10-31 14:39:38 +00:00 
			
		
		
		
	Compare commits
	
		
			57 Commits
		
	
	
		
			Better-row
			...
			sidebar-mw
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 5409752817 | ||
|   | a70e6c49a1 | ||
|   | 3d83d5f4b5 | ||
|   | f9dece0743 | ||
|   | ac0871d0e8 | ||
|   | ffc19e591d | ||
|   | c53380ca3d | ||
|   | 7c74a2026a | ||
|   | adaed438d9 | ||
|   | baf38305cb | ||
|   | 8254712521 | ||
|   | 53214781e3 | ||
|   | 88cbbbdf65 | ||
|   | c10dca9c7b | ||
|   | 7f2ebb4bde | ||
|   | f1abb60e4a | ||
|   | e014c7aff6 | ||
|   | b79c03433e | ||
|   | 34eb4d974d | ||
|   | 3264be3c5e | ||
|   | 655f4f75fb | ||
|   | 4383f31696 | ||
|   | 99eb15d15e | ||
|   | 2682c6e150 | ||
|   | 3a5d854e6d | ||
|   | 1e90c6387c | ||
|   | 2cca25f4d0 | ||
|   | 565724d201 | ||
|   | 3e4955becd | ||
|   | 7b560c727f | ||
|   | 35abd9dfdb | ||
|   | b7ccf3e0e5 | ||
|   | 0d9ab8fdd0 | ||
|   | 303f9290a8 | ||
|   | e0c4dc08a1 | ||
|   | 8c655883fe | ||
|   | ba90785115 | ||
|   | 7b392b626b | ||
|   | 8e4ceb7d48 | ||
|   | 2ab1c6e9a9 | ||
|   | dbdced0971 | ||
|   | 5e481880bd | ||
|   | faec063f34 | ||
|   | bbea38d227 | ||
|   | a0ef60de49 | ||
|   | 3313572606 | ||
|   | c4f850cb14 | ||
|   | 3bdab738c6 | ||
|   | faaef31b9f | ||
|   | ca7b8b8b4c | ||
|   | 9ca84e0694 | ||
|   | daaf2b1796 | ||
|   | 25f7cbea5a | ||
|   | c485ea9d7b | ||
|   | 295390c8e9 | ||
|   | 3ebf816ce2 | ||
|   | 50e7410002 | 
| @@ -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 + Browse Media | ||||
|     supported_features: 195135, | ||||
|     // Select Source + Stop + Clear + Play + Shuffle Set | ||||
|     supported_features: 64063, | ||||
|     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 | ||||
|     supported_features: 64063, | ||||
|     // Select Source + Stop + Clear + Play + Shuffle Set + Browse Media | ||||
|     supported_features: 195135, | ||||
|     entity_picture: "/images/album_cover.jpg", | ||||
|     media_duration: 300, | ||||
|     media_position: 0, | ||||
|   | ||||
| @@ -146,6 +146,16 @@ 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 { | ||||
|   | ||||
| @@ -74,9 +74,7 @@ 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( | ||||
|   | ||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ from setuptools import setup, find_packages | ||||
|  | ||||
| setup( | ||||
|     name="home-assistant-frontend", | ||||
|     version="20201126.0", | ||||
|     version="20201212.0", | ||||
|     description="The Home Assistant frontend", | ||||
|     url="https://github.com/home-assistant/home-assistant-polymer", | ||||
|     author="The Home Assistant Authors", | ||||
|   | ||||
							
								
								
									
										6
									
								
								src/common/ensure-array.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/common/ensure-array.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| export const ensureArray = (value?: any) => { | ||||
|   if (!value || Array.isArray(value)) { | ||||
|     return value; | ||||
|   } | ||||
|   return [value]; | ||||
| }; | ||||
| @@ -67,6 +67,10 @@ export const computeStateDisplay = ( | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (domain === "counter") { | ||||
|     return formatNumber(compareState, language); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     // Return device class translation | ||||
|     (stateObj.attributes.device_class && | ||||
|   | ||||
| @@ -1,8 +1,12 @@ | ||||
| export const copyToClipboard = (str) => { | ||||
|   const el = document.createElement("textarea"); | ||||
|   el.value = str; | ||||
|   document.body.appendChild(el); | ||||
|   el.select(); | ||||
|   document.execCommand("copy"); | ||||
|   document.body.removeChild(el); | ||||
|   if (navigator.clipboard) { | ||||
|     navigator.clipboard.writeText(str); | ||||
|   } else { | ||||
|     const el = document.createElement("textarea"); | ||||
|     el.value = str; | ||||
|     document.body.appendChild(el); | ||||
|     el.select(); | ||||
|     document.execCommand("copy"); | ||||
|     document.body.removeChild(el); | ||||
|   } | ||||
| }; | ||||
|   | ||||
| @@ -98,6 +98,12 @@ export class HaDataTable extends LitElement { | ||||
|  | ||||
|   @property({ type: Boolean }) public hasFab = false; | ||||
|  | ||||
|   /** | ||||
|    * Add an extra rows at the bottom of the datatabel | ||||
|    * @type {TemplateResult} | ||||
|    */ | ||||
|   @property({ attribute: false }) public appendRow?; | ||||
|  | ||||
|   @property({ type: Boolean, attribute: "auto-height" }) | ||||
|   public autoHeight = false; | ||||
|  | ||||
| @@ -126,6 +132,8 @@ export class HaDataTable extends LitElement { | ||||
|  | ||||
|   @query("slot[name='header']") private _header!: HTMLSlotElement; | ||||
|  | ||||
|   private _items: DataTableRowData[] = []; | ||||
|  | ||||
|   private _checkableRowsCount?: number; | ||||
|  | ||||
|   private _checkedRows: string[] = []; | ||||
| @@ -318,10 +326,13 @@ export class HaDataTable extends LitElement { | ||||
|                   @scroll=${this._saveScrollPos} | ||||
|                 > | ||||
|                   ${scroll({ | ||||
|                     items: !this.hasFab | ||||
|                       ? this._filteredData | ||||
|                       : [...this._filteredData, ...[{ empty: true }]], | ||||
|                     items: this._items, | ||||
|                     renderItem: (row: DataTableRowData, index) => { | ||||
|                       if (row.append) { | ||||
|                         return html` | ||||
|                           <div class="mdc-data-table__row">${row.content}</div> | ||||
|                         `; | ||||
|                       } | ||||
|                       if (row.empty) { | ||||
|                         return html` <div class="mdc-data-table__row"></div> `; | ||||
|                       } | ||||
| @@ -447,6 +458,20 @@ export class HaDataTable extends LitElement { | ||||
|     if (this.curRequest !== curRequest) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (this.appendRow || this.hasFab) { | ||||
|       this._items = [...data]; | ||||
|  | ||||
|       if (this.appendRow) { | ||||
|         this._items.push({ append: true, content: this.appendRow }); | ||||
|       } | ||||
|  | ||||
|       if (this.hasFab) { | ||||
|         this._items.push({ empty: true }); | ||||
|       } | ||||
|     } else { | ||||
|       this._items = data; | ||||
|     } | ||||
|     this._filteredData = data; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -139,7 +139,7 @@ export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) { | ||||
|  | ||||
|   private _filteredDevices: DeviceRegistryEntry[] = []; | ||||
|  | ||||
|   private _getDevices = memoizeOne( | ||||
|   private _getAreasWithDevices = 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._getDevices( | ||||
|     const areas = this._getAreasWithDevices( | ||||
|       this._devices, | ||||
|       this._areas, | ||||
|       this._entities, | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import "../ha-icon-button"; | ||||
| import "../ha-svg-icon"; | ||||
| import "@material/mwc-icon-button/mwc-icon-button"; | ||||
| import "@polymer/paper-input/paper-input"; | ||||
| import "@polymer/paper-item/paper-item"; | ||||
| import "@polymer/paper-item/paper-item-body"; | ||||
| @@ -12,6 +13,8 @@ import { | ||||
|   html, | ||||
|   LitElement, | ||||
|   property, | ||||
|   PropertyValues, | ||||
|   query, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import memoizeOne from "memoize-one"; | ||||
| @@ -35,6 +38,7 @@ import { | ||||
| import { SubscribeMixin } from "../../mixins/subscribe-mixin"; | ||||
| import { PolymerChangedEvent } from "../../polymer-types"; | ||||
| import { HomeAssistant } from "../../types"; | ||||
| import { mdiClose, mdiMenuUp, mdiMenuDown } from "@mdi/js"; | ||||
|  | ||||
| interface Device { | ||||
|   name: string; | ||||
| @@ -111,6 +115,10 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { | ||||
|   @property({ type: Boolean }) | ||||
|   private _opened?: boolean; | ||||
|  | ||||
|   @query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement; | ||||
|  | ||||
|   private _init = false; | ||||
|  | ||||
|   private _getDevices = memoizeOne( | ||||
|     ( | ||||
|       devices: DeviceRegistryEntry[], | ||||
| @@ -122,18 +130,27 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { | ||||
|       deviceFilter: this["deviceFilter"] | ||||
|     ): Device[] => { | ||||
|       if (!devices.length) { | ||||
|         return []; | ||||
|         return [ | ||||
|           { | ||||
|             id: "", | ||||
|             area: "", | ||||
|             name: this.hass.localize("ui.components.device-picker.no_devices"), | ||||
|           }, | ||||
|         ]; | ||||
|       } | ||||
|  | ||||
|       const deviceEntityLookup: DeviceEntityLookup = {}; | ||||
|       for (const entity of entities) { | ||||
|         if (!entity.device_id) { | ||||
|           continue; | ||||
|  | ||||
|       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); | ||||
|         } | ||||
|         if (!(entity.device_id in deviceEntityLookup)) { | ||||
|           deviceEntityLookup[entity.device_id] = []; | ||||
|         } | ||||
|         deviceEntityLookup[entity.device_id].push(entity); | ||||
|       } | ||||
|  | ||||
|       const areaLookup: { [areaId: string]: AreaRegistryEntry } = {}; | ||||
| @@ -141,7 +158,9 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { | ||||
|         areaLookup[area.area_id] = area; | ||||
|       } | ||||
|  | ||||
|       let inputDevices = [...devices]; | ||||
|       let inputDevices = devices.filter( | ||||
|         (device) => device.id === this.value || !device.disabled_by | ||||
|       ); | ||||
|  | ||||
|       if (includeDomains) { | ||||
|         inputDevices = inputDevices.filter((device) => { | ||||
| @@ -208,6 +227,15 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { | ||||
|             : this.hass.localize("ui.components.device-picker.no_area"), | ||||
|         }; | ||||
|       }); | ||||
|       if (!outputDevices.length) { | ||||
|         return [ | ||||
|           { | ||||
|             id: "", | ||||
|             area: "", | ||||
|             name: this.hass.localize("ui.components.device-picker.no_match"), | ||||
|           }, | ||||
|         ]; | ||||
|       } | ||||
|       if (outputDevices.length === 1) { | ||||
|         return outputDevices; | ||||
|       } | ||||
| @@ -215,6 +243,18 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { | ||||
|     } | ||||
|   ); | ||||
|  | ||||
|   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(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public hassSubscribe(): UnsubscribeFunc[] { | ||||
|     return [ | ||||
|       subscribeDeviceRegistry(this.hass.connection!, (devices) => { | ||||
| @@ -229,25 +269,33 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { | ||||
|     ]; | ||||
|   } | ||||
|  | ||||
|   protected updated(changedProps: PropertyValues) { | ||||
|     if ( | ||||
|       (!this._init && this.devices && this.areas && this.entities) || | ||||
|       (changedProps.has("_opened") && this._opened) | ||||
|     ) { | ||||
|       this._init = true; | ||||
|       (this._comboBox as any).items = this._getDevices( | ||||
|         this.devices!, | ||||
|         this.areas!, | ||||
|         this.entities!, | ||||
|         this.includeDomains, | ||||
|         this.excludeDomains, | ||||
|         this.includeDeviceClasses, | ||||
|         this.deviceFilter | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     if (!this.devices || !this.areas || !this.entities) { | ||||
|       return html``; | ||||
|     } | ||||
|     const devices = this._getDevices( | ||||
|       this.devices, | ||||
|       this.areas, | ||||
|       this.entities, | ||||
|       this.includeDomains, | ||||
|       this.excludeDomains, | ||||
|       this.includeDeviceClasses, | ||||
|       this.deviceFilter | ||||
|     ); | ||||
|     return html` | ||||
|       <vaadin-combo-box-light | ||||
|         item-value-path="id" | ||||
|         item-id-path="id" | ||||
|         item-label-path="name" | ||||
|         .items=${devices} | ||||
|         .value=${this._value} | ||||
|         .renderer=${rowRenderer} | ||||
|         @opened-changed=${this._openedChanged} | ||||
| @@ -265,34 +313,30 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { | ||||
|         > | ||||
|           ${this.value | ||||
|             ? html` | ||||
|                 <ha-icon-button | ||||
|                   aria-label=${this.hass.localize( | ||||
|                 <mwc-icon-button | ||||
|                   .label=${this.hass.localize( | ||||
|                     "ui.components.device-picker.clear" | ||||
|                   )} | ||||
|                   slot="suffix" | ||||
|                   class="clear-button" | ||||
|                   icon="hass:close" | ||||
|                   @click=${this._clearValue} | ||||
|                   no-ripple | ||||
|                 > | ||||
|                   Clear | ||||
|                 </ha-icon-button> | ||||
|               ` | ||||
|             : ""} | ||||
|           ${devices.length > 0 | ||||
|             ? html` | ||||
|                 <ha-icon-button | ||||
|                   aria-label=${this.hass.localize( | ||||
|                     "ui.components.device-picker.show_devices" | ||||
|                   )} | ||||
|                   slot="suffix" | ||||
|                   class="toggle-button" | ||||
|                   .icon=${this._opened ? "hass:menu-up" : "hass:menu-down"} | ||||
|                 > | ||||
|                   Toggle | ||||
|                 </ha-icon-button> | ||||
|                   <ha-svg-icon .path=${mdiClose}></ha-svg-icon> | ||||
|                 </mwc-icon-button> | ||||
|               ` | ||||
|             : ""} | ||||
|  | ||||
|           <mwc-icon-button | ||||
|             .label=${this.hass.localize( | ||||
|               "ui.components.device-picker.show_devices" | ||||
|             )} | ||||
|             slot="suffix" | ||||
|             class="toggle-button" | ||||
|           > | ||||
|             <ha-svg-icon | ||||
|               .path=${this._opened ? mdiMenuUp : mdiMenuDown} | ||||
|             ></ha-svg-icon> | ||||
|           </mwc-icon-button> | ||||
|         </paper-input> | ||||
|       </vaadin-combo-box-light> | ||||
|     `; | ||||
| @@ -329,7 +373,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { | ||||
|  | ||||
|   static get styles(): CSSResult { | ||||
|     return css` | ||||
|       paper-input > ha-icon-button { | ||||
|       paper-input > mwc-icon-button { | ||||
|         --mdc-icon-button-size: 24px; | ||||
|         padding: 2px; | ||||
|         color: var(--secondary-text-color); | ||||
|   | ||||
| @@ -95,13 +95,24 @@ export class HaEntityPicker extends LitElement { | ||||
|  | ||||
|   @property() public entityFilter?: HaEntityPickerEntityFilterFunc; | ||||
|  | ||||
|   @property({ type: Boolean, attribute: "hide-clear-icon" }) | ||||
|   public hideClearIcon = false; | ||||
|   @property({ type: Boolean }) 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[] = []; | ||||
| @@ -154,6 +165,24 @@ export class HaEntityPicker extends LitElement { | ||||
|         ); | ||||
|       } | ||||
|  | ||||
|       if (!states.length) { | ||||
|         return [ | ||||
|           { | ||||
|             entity_id: "", | ||||
|             state: "", | ||||
|             last_changed: "", | ||||
|             last_updated: "", | ||||
|             context: { id: "", user_id: null }, | ||||
|             attributes: { | ||||
|               friendly_name: this.hass!.localize( | ||||
|                 "ui.components.entity.entity-picker.no_match" | ||||
|               ), | ||||
|               icon: "mdi:magnify", | ||||
|             }, | ||||
|           }, | ||||
|         ]; | ||||
|       } | ||||
|  | ||||
|       return states; | ||||
|     } | ||||
|   ); | ||||
| @@ -204,7 +233,6 @@ export class HaEntityPicker extends LitElement { | ||||
|           .label=${this.label === undefined | ||||
|             ? this.hass.localize("ui.components.entity.entity-picker.entity") | ||||
|             : this.label} | ||||
|           .value=${this._value} | ||||
|           .disabled=${this.disabled} | ||||
|           class="input" | ||||
|           autocapitalize="none" | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import "./ha-icon-button"; | ||||
| import "./ha-svg-icon"; | ||||
| import "@material/mwc-icon-button/mwc-icon-button"; | ||||
| import "@polymer/paper-input/paper-input"; | ||||
| import "@polymer/paper-item/paper-item"; | ||||
| import "@polymer/paper-item/paper-item-body"; | ||||
| @@ -14,6 +15,8 @@ import { | ||||
|   property, | ||||
|   internalProperty, | ||||
|   TemplateResult, | ||||
|   PropertyValues, | ||||
|   query, | ||||
| } from "lit-element"; | ||||
| import { fireEvent } from "../common/dom/fire_event"; | ||||
| import { | ||||
| @@ -29,6 +32,18 @@ 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"; | ||||
| import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js"; | ||||
|  | ||||
| const rowRenderer = ( | ||||
|   root: HTMLElement, | ||||
| @@ -71,31 +86,250 @@ 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; | ||||
|  | ||||
|   @query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement; | ||||
|  | ||||
|   private _init = false; | ||||
|  | ||||
|   public hassSubscribe(): UnsubscribeFunc[] { | ||||
|     return [ | ||||
|       subscribeAreaRegistry(this.hass.connection!, (areas) => { | ||||
|         this._areas = this.noAdd | ||||
|           ? areas | ||||
|           : [ | ||||
|               ...areas, | ||||
|               { | ||||
|                 area_id: "add_new", | ||||
|                 name: this.hass.localize("ui.components.area-picker.add_new"), | ||||
|               }, | ||||
|             ]; | ||||
|         this._areas = areas; | ||||
|       }), | ||||
|       subscribeDeviceRegistry(this.hass.connection!, (devices) => { | ||||
|         this._devices = devices; | ||||
|       }), | ||||
|       subscribeEntityRegistry(this.hass.connection!, (entities) => { | ||||
|         this._entities = entities; | ||||
|       }), | ||||
|     ]; | ||||
|   } | ||||
|  | ||||
|   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[] => { | ||||
|       if (!areas.length) { | ||||
|         return [ | ||||
|           { | ||||
|             area_id: "", | ||||
|             name: this.hass.localize("ui.components.area-picker.no_areas"), | ||||
|           }, | ||||
|         ]; | ||||
|       } | ||||
|  | ||||
|       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) { | ||||
|         inputEntities = inputEntities!.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)); | ||||
|       } | ||||
|  | ||||
|       if (!outputAreas.length) { | ||||
|         outputAreas = [ | ||||
|           { | ||||
|             area_id: "", | ||||
|             name: this.hass.localize("ui.components.area-picker.no_match"), | ||||
|           }, | ||||
|         ]; | ||||
|       } | ||||
|  | ||||
|       return noAdd | ||||
|         ? outputAreas | ||||
|         : [ | ||||
|             ...outputAreas, | ||||
|             { | ||||
|               area_id: "add_new", | ||||
|               name: this.hass.localize("ui.components.area-picker.add_new"), | ||||
|             }, | ||||
|           ]; | ||||
|     } | ||||
|   ); | ||||
|  | ||||
|   protected updated(changedProps: PropertyValues) { | ||||
|     if ( | ||||
|       (!this._init && this._devices && this._areas && this._entities) || | ||||
|       (changedProps.has("_opened") && this._opened) | ||||
|     ) { | ||||
|       this._init = true; | ||||
|       (this._comboBox as any).items = this._getAreas( | ||||
|         this._areas!, | ||||
|         this._devices!, | ||||
|         this._entities!, | ||||
|         this.includeDomains, | ||||
|         this.excludeDomains, | ||||
|         this.includeDeviceClasses, | ||||
|         this.deviceFilter, | ||||
|         this.entityFilter, | ||||
|         this.noAdd | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     if (!this._areas) { | ||||
|     if (!this._devices || !this._areas || !this._entities) { | ||||
|       return html``; | ||||
|     } | ||||
|     return html` | ||||
| @@ -103,7 +337,6 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) { | ||||
|         item-value-path="area_id" | ||||
|         item-id-path="area_id" | ||||
|         item-label-path="name" | ||||
|         .items=${this._areas} | ||||
|         .value=${this._value} | ||||
|         .renderer=${rowRenderer} | ||||
|         @opened-changed=${this._openedChanged} | ||||
| @@ -124,34 +357,28 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) { | ||||
|         > | ||||
|           ${this.value | ||||
|             ? html` | ||||
|                 <ha-icon-button | ||||
|                   aria-label=${this.hass.localize( | ||||
|                 <mwc-icon-button | ||||
|                   .label=${this.hass.localize( | ||||
|                     "ui.components.area-picker.clear" | ||||
|                   )} | ||||
|                   slot="suffix" | ||||
|                   class="clear-button" | ||||
|                   icon="hass:close" | ||||
|                   @click=${this._clearValue} | ||||
|                   no-ripple | ||||
|                 > | ||||
|                   ${this.hass.localize("ui.components.area-picker.clear")} | ||||
|                 </ha-icon-button> | ||||
|               ` | ||||
|             : ""} | ||||
|           ${this._areas.length > 0 | ||||
|             ? html` | ||||
|                 <ha-icon-button | ||||
|                   aria-label=${this.hass.localize( | ||||
|                     "ui.components.area-picker.show_areas" | ||||
|                   )} | ||||
|                   slot="suffix" | ||||
|                   class="toggle-button" | ||||
|                   .icon=${this._opened ? "hass:menu-up" : "hass:menu-down"} | ||||
|                 > | ||||
|                   ${this.hass.localize("ui.components.area-picker.toggle")} | ||||
|                 </ha-icon-button> | ||||
|                   <ha-svg-icon .path=${mdiClose}></ha-svg-icon> | ||||
|                 </mwc-icon-button> | ||||
|               ` | ||||
|             : ""} | ||||
|  | ||||
|           <mwc-icon-button | ||||
|             .label=${this.hass.localize("ui.components.area-picker.toggle")} | ||||
|             slot="suffix" | ||||
|             class="toggle-button" | ||||
|           > | ||||
|             <ha-svg-icon | ||||
|               .path=${this._opened ? mdiMenuUp : mdiMenuDown} | ||||
|             ></ha-svg-icon> | ||||
|           </mwc-icon-button> | ||||
|         </paper-input> | ||||
|       </vaadin-combo-box-light> | ||||
|     `; | ||||
| @@ -227,7 +454,7 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) { | ||||
|  | ||||
|   static get styles(): CSSResult { | ||||
|     return css` | ||||
|       paper-input > ha-icon-button { | ||||
|       paper-input > mwc-icon-button { | ||||
|         --mdc-icon-button-size: 24px; | ||||
|         padding: 2px; | ||||
|         color: var(--secondary-text-color); | ||||
|   | ||||
| @@ -52,6 +52,7 @@ class HaBluePrintPicker extends LitElement { | ||||
|         .label=${this.label || | ||||
|         this.hass.localize("ui.components.blueprint-picker.label")} | ||||
|         .disabled=${this.disabled} | ||||
|         horizontal-align="left" | ||||
|       > | ||||
|         <paper-listbox | ||||
|           slot="dropdown-content" | ||||
| @@ -110,6 +111,9 @@ class HaBluePrintPicker extends LitElement { | ||||
|       paper-listbox { | ||||
|         min-width: 200px; | ||||
|       } | ||||
|       paper-item { | ||||
|         cursor: pointer; | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -23,8 +23,6 @@ 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() { | ||||
| @@ -41,7 +39,6 @@ export class HaButtonMenu extends LitElement { | ||||
|         <slot name="trigger"></slot> | ||||
|       </div> | ||||
|       <mwc-menu | ||||
|         .fixed=${this.fixed} | ||||
|         .corner=${this.corner} | ||||
|         .multi=${this.multi} | ||||
|         .activatable=${this.activatable} | ||||
|   | ||||
| @@ -11,6 +11,7 @@ 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 { | ||||
| @@ -21,17 +22,22 @@ export class HaButtonToggleGroup extends LitElement { | ||||
|   protected render(): TemplateResult { | ||||
|     return html` | ||||
|       <div> | ||||
|         ${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> | ||||
|           ` | ||||
|         ${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 | ||||
|               >` | ||||
|         )} | ||||
|       </div> | ||||
|     `; | ||||
| @@ -49,13 +55,15 @@ 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-icon-button, | ||||
|       mwc-button { | ||||
|         border: 1px solid var(--primary-color); | ||||
|         border-right-width: 0px; | ||||
|         position: relative; | ||||
|         cursor: pointer; | ||||
|       } | ||||
|       mwc-icon-button::before { | ||||
|       mwc-icon-button::before, | ||||
|       mwc-button::before { | ||||
|         top: 0; | ||||
|         left: 0; | ||||
|         width: 100%; | ||||
| @@ -67,17 +75,21 @@ export class HaButtonToggleGroup extends LitElement { | ||||
|         content: ""; | ||||
|         transition: opacity 15ms linear, background-color 15ms linear; | ||||
|       } | ||||
|       mwc-icon-button[active]::before { | ||||
|       mwc-icon-button[active]::before, | ||||
|       mwc-button[active]::before { | ||||
|         opacity: var(--mdc-icon-button-ripple-opacity, 0.12); | ||||
|       } | ||||
|       mwc-icon-button:first-child { | ||||
|       mwc-icon-button:first-child, | ||||
|       mwc-button:first-child { | ||||
|         border-radius: 4px 0 0 4px; | ||||
|       } | ||||
|       mwc-icon-button:last-child { | ||||
|       mwc-icon-button:last-child, | ||||
|       mwc-button:last-child { | ||||
|         border-radius: 0 4px 4px 0; | ||||
|         border-right-width: 1px; | ||||
|       } | ||||
|       mwc-icon-button:only-child { | ||||
|       mwc-icon-button:only-child, | ||||
|       mwc-button:only-child { | ||||
|         border-radius: 4px; | ||||
|         border-right-width: 1px; | ||||
|       } | ||||
|   | ||||
| @@ -68,12 +68,6 @@ 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; | ||||
| @@ -96,23 +90,6 @@ 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; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| import { mdiChevronDown } from "@mdi/js"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResult, | ||||
| @@ -9,9 +8,10 @@ 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 { | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { mdiHelpCircleOutline } from "@mdi/js"; | ||||
| import { mdiHelpCircle } 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=${mdiHelpCircleOutline}></ha-svg-icon> | ||||
|       <ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon> | ||||
|       <paper-tooltip | ||||
|         offset="4" | ||||
|         .position=${this.position} | ||||
|   | ||||
| @@ -127,7 +127,7 @@ class HaHLSPlayer extends LitElement { | ||||
|  | ||||
|     // Parse playlist assuming it is a master playlist. Match group 1 is whether hevc, match group 2 is regular playlist url | ||||
|     // See https://tools.ietf.org/html/rfc8216 for HLS spec details | ||||
|     const playlistRegexp = /#EXT-X-STREAM-INF:.*?(?:CODECS=".*?(?<isHevc>hev1|hvc1)?\..*?".*?)?(?:\n|\r\n)(?<streamUrl>.+)/g; | ||||
|     const playlistRegexp = /#EXT-X-STREAM-INF:.*?(?:CODECS=".*?(hev1|hvc1)?\..*?".*?)?(?:\n|\r\n)(.+)/g; | ||||
|     const match = playlistRegexp.exec(masterPlaylist); | ||||
|     const matchTwice = playlistRegexp.exec(masterPlaylist); | ||||
|  | ||||
| @@ -136,17 +136,13 @@ class HaHLSPlayer extends LitElement { | ||||
|     let playlist_url: string; | ||||
|     if (match !== null && matchTwice === null) { | ||||
|       // Only send the regular playlist url if we match exactly once | ||||
|       playlist_url = new URL(match.groups!.streamUrl, this.url).href; | ||||
|       playlist_url = new URL(match[2], this.url).href; | ||||
|     } else { | ||||
|       playlist_url = this.url; | ||||
|     } | ||||
|  | ||||
|     // If codec is HEVC and ExoPlayer is supported, use ExoPlayer. | ||||
|     if ( | ||||
|       this._useExoPlayer && | ||||
|       match !== null && | ||||
|       match.groups!.isHevc !== undefined | ||||
|     ) { | ||||
|     if (this._useExoPlayer && match !== null && match[1] !== undefined) { | ||||
|       this._renderHLSExoPlayer(playlist_url); | ||||
|     } else if (hls.isSupported()) { | ||||
|       this._renderHLSPolyfill(videoEl, hls, playlist_url); | ||||
|   | ||||
| @@ -60,8 +60,9 @@ export class HaIconInput extends LitElement { | ||||
|   static get styles() { | ||||
|     return css` | ||||
|       ha-icon { | ||||
|         position: relative; | ||||
|         bottom: 4px; | ||||
|         position: absolute; | ||||
|         bottom: 2px; | ||||
|         right: 0; | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import type { HomeAssistant } from "../types"; | ||||
| class HaRelativeTime extends UpdatingElement { | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @property({ attribute: false }) public datetime?: string; | ||||
|   @property({ attribute: false }) public datetime?: string | Date; | ||||
|  | ||||
|   private _interval?: number; | ||||
|  | ||||
|   | ||||
							
								
								
									
										45
									
								
								src/components/ha-selector/ha-selector-action.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/components/ha-selector/ha-selector-action.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| 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; | ||||
|   } | ||||
| } | ||||
| @@ -1,7 +1,16 @@ | ||||
| import { customElement, html, LitElement, property } from "lit-element"; | ||||
| import { | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   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 { | ||||
| @@ -13,14 +22,77 @@ 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 && | ||||
|         !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 { | ||||
|   | ||||
| @@ -63,7 +63,8 @@ export class HaDeviceSelector extends LitElement { | ||||
|     } | ||||
|     if (this.selector.device.integration) { | ||||
|       if ( | ||||
|         !this._configEntries?.some((entry) => | ||||
|         this._configEntries && | ||||
|         !this._configEntries.some((entry) => | ||||
|           device.config_entries.includes(entry.entry_id) | ||||
|         ) | ||||
|       ) { | ||||
|   | ||||
| @@ -19,7 +19,7 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) { | ||||
|  | ||||
|   @property() public selector!: EntitySelector; | ||||
|  | ||||
|   @internalProperty() private _entities?: Record<string, string>; | ||||
|   @internalProperty() private _entityPlaformLookup?: Record<string, string>; | ||||
|  | ||||
|   @property() public value?: any; | ||||
|  | ||||
| @@ -45,7 +45,7 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) { | ||||
|           } | ||||
|           entityLookup[confEnt.entity_id] = confEnt.platform; | ||||
|         } | ||||
|         this._entities = entityLookup; | ||||
|         this._entityPlaformLookup = entityLookup; | ||||
|       }), | ||||
|     ]; | ||||
|   } | ||||
| @@ -66,8 +66,9 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) { | ||||
|     } | ||||
|     if (this.selector.entity.integration) { | ||||
|       if ( | ||||
|         !this._entities || | ||||
|         this._entities[entity.entity_id] !== this.selector.entity.integration | ||||
|         !this._entityPlaformLookup || | ||||
|         this._entityPlaformLookup[entity.entity_id] !== | ||||
|           this.selector.entity.integration | ||||
|       ) { | ||||
|         return false; | ||||
|       } | ||||
|   | ||||
							
								
								
									
										153
									
								
								src/components/ha-selector/ha-selector-target.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/components/ha-selector/ha-selector-target.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| 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; | ||||
|   } | ||||
| } | ||||
| @@ -5,9 +5,11 @@ 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") | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import "@material/mwc-list/mwc-list-item"; | ||||
| import "@polymer/paper-item/paper-item-body"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResult, | ||||
| @@ -18,10 +18,13 @@ export class HaSettingsRow extends LitElement { | ||||
|  | ||||
|   protected render(): SVGTemplateResult { | ||||
|     return html` | ||||
|       <mwc-list-item noninteractive ?twoline=${!this.threeLine}> | ||||
|       <paper-item-body | ||||
|         ?two-line=${!this.threeLine} | ||||
|         ?three-line=${this.threeLine} | ||||
|       > | ||||
|         <slot name="heading"></slot> | ||||
|         <span slot="secondary"><slot name="description"></slot></span> | ||||
|       </mwc-list-item> | ||||
|         <div secondary><slot name="description"></slot></div> | ||||
|       </paper-item-body> | ||||
|       <slot></slot> | ||||
|     `; | ||||
|   } | ||||
| @@ -34,7 +37,14 @@ export class HaSettingsRow extends LitElement { | ||||
|         align-content: normal; | ||||
|         align-self: auto; | ||||
|         align-items: center; | ||||
|         justify-content: space-between; | ||||
|       } | ||||
|       paper-item-body { | ||||
|         padding: 8px 16px 8px 0; | ||||
|       } | ||||
|       paper-item-body[two-line] { | ||||
|         min-height: calc( | ||||
|           var(--paper-item-body-two-line-min-height, 72px) - 16px | ||||
|         ); | ||||
|       } | ||||
|       :host([narrow]) { | ||||
|         align-items: normal; | ||||
| @@ -42,12 +52,12 @@ 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; | ||||
|       } | ||||
|       div[secondary] { | ||||
|         white-space: normal; | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										605
									
								
								src/components/ha-target-picker.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										605
									
								
								src/components/ha-target-picker.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,605 @@ | ||||
| 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 { | ||||
|   computeDeviceName, | ||||
|   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 ? computeDeviceName(device, this.hass) : 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"] { | ||||
|     const newVal = ensureArray(value![type])!.filter((val) => val !== id); | ||||
|     if (newVal.length) { | ||||
|       return { | ||||
|         ...value, | ||||
|         [type]: newVal, | ||||
|       }; | ||||
|     } | ||||
|     const val = { ...value }!; | ||||
|     delete val[type]; | ||||
|     if (Object.keys(val).length) { | ||||
|       return val; | ||||
|     } | ||||
|     return undefined; | ||||
|   } | ||||
|  | ||||
|   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; | ||||
|   } | ||||
| } | ||||
| @@ -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 } from "./script"; | ||||
| import { Action, MODES } from "./script"; | ||||
|  | ||||
| export interface AutomationEntity extends HassEntityBase { | ||||
|   attributes: HassEntityAttributeBase & { | ||||
| @@ -26,7 +26,7 @@ export interface ManualAutomationConfig { | ||||
|   trigger: Trigger[]; | ||||
|   condition?: Condition[]; | ||||
|   action: Action[]; | ||||
|   mode?: "single" | "restart" | "queued" | "parallel"; | ||||
|   mode?: typeof MODES[number]; | ||||
|   max?: number; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,7 @@ export interface DeviceRegistryEntry { | ||||
|   area_id?: string; | ||||
|   name_by_user?: string; | ||||
|   entry_type: "service" | null; | ||||
|   disabled_by: string | null; | ||||
| } | ||||
|  | ||||
| export interface DeviceEntityLookup { | ||||
| @@ -26,6 +27,7 @@ export interface DeviceEntityLookup { | ||||
| export interface DeviceRegistryEntryMutableParams { | ||||
|   area_id?: string | null; | ||||
|   name_by_user?: string | null; | ||||
|   disabled_by?: string | null; | ||||
| } | ||||
|  | ||||
| export const fallbackDeviceName = ( | ||||
|   | ||||
| @@ -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"]; | ||||
| export const MODES = ["single", "restart", "queued", "parallel"] as const; | ||||
| export const MODES_MAX = ["queued", "parallel"]; | ||||
|  | ||||
| export interface ScriptEntity extends HassEntityBase { | ||||
|   attributes: HassEntityAttributeBase & { | ||||
|     last_triggered: string; | ||||
|     mode: "single" | "restart" | "queued" | "parallel"; | ||||
|     mode: typeof MODES[number]; | ||||
|     current?: number; | ||||
|     max?: number; | ||||
|   }; | ||||
| @@ -23,7 +23,7 @@ export interface ScriptConfig { | ||||
|   alias: string; | ||||
|   sequence: Action[]; | ||||
|   icon?: string; | ||||
|   mode?: "single" | "restart" | "queued" | "parallel"; | ||||
|   mode?: typeof MODES[number]; | ||||
|   max?: number; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -2,9 +2,11 @@ export type Selector = | ||||
|   | EntitySelector | ||||
|   | DeviceSelector | ||||
|   | AreaSelector | ||||
|   | TargetSelector | ||||
|   | NumberSelector | ||||
|   | BooleanSelector | ||||
|   | TimeSelector; | ||||
|   | TimeSelector | ||||
|   | ActionSelector; | ||||
|  | ||||
| export interface EntitySelector { | ||||
|   entity: { | ||||
| @@ -19,13 +21,41 @@ export interface DeviceSelector { | ||||
|     integration?: string; | ||||
|     manufacturer?: string; | ||||
|     model?: string; | ||||
|     entity?: EntitySelector["entity"]; | ||||
|     entity?: { | ||||
|       domain?: EntitySelector["entity"]["domain"]; | ||||
|       device_class?: EntitySelector["entity"]["device_class"]; | ||||
|     }; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export interface AreaSelector { | ||||
|   // eslint-disable-next-line @typescript-eslint/ban-types | ||||
|   area: {}; | ||||
|   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"]; | ||||
|     }; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export interface NumberSelector { | ||||
| @@ -47,3 +77,8 @@ export interface TimeSelector { | ||||
|   // eslint-disable-next-line @typescript-eslint/ban-types | ||||
|   time: {}; | ||||
| } | ||||
|  | ||||
| export interface ActionSelector { | ||||
|   // eslint-disable-next-line @typescript-eslint/ban-types | ||||
|   action: {}; | ||||
| } | ||||
|   | ||||
							
								
								
									
										5
									
								
								src/data/target.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/data/target.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| export interface Target { | ||||
|   entity_id?: string[]; | ||||
|   device_id?: string[]; | ||||
|   area_id?: string[]; | ||||
| } | ||||
| @@ -20,6 +20,7 @@ export interface User { | ||||
|  | ||||
| export interface UpdateUserParams { | ||||
|   name?: User["name"]; | ||||
|   is_active?: User["is_active"]; | ||||
|   group_ids?: User["group_ids"]; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -17,17 +17,17 @@ import "../../components/ha-switch"; | ||||
| import { PolymerChangedEvent } from "../../polymer-types"; | ||||
| import { haStyleDialog } from "../../resources/styles"; | ||||
| import { HomeAssistant } from "../../types"; | ||||
| import { DialogParams } from "./show-dialog-box"; | ||||
| import { DialogBoxParams } from "./show-dialog-box"; | ||||
|  | ||||
| @customElement("dialog-box") | ||||
| class DialogBox extends LitElement { | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
|  | ||||
|   @internalProperty() private _params?: DialogParams; | ||||
|   @internalProperty() private _params?: DialogBoxParams; | ||||
|  | ||||
|   @internalProperty() private _value?: string; | ||||
|  | ||||
|   public async showDialog(params: DialogParams): Promise<void> { | ||||
|   public async showDialog(params: DialogBoxParams): Promise<void> { | ||||
|     this._params = params; | ||||
|     if (params.prompt) { | ||||
|       this._value = params.defaultValue; | ||||
| @@ -55,8 +55,8 @@ class DialogBox extends LitElement { | ||||
|     return html` | ||||
|       <ha-dialog | ||||
|         open | ||||
|         ?scrimClickAction=${this._params.prompt} | ||||
|         ?escapeKeyAction=${this._params.prompt} | ||||
|         ?scrimClickAction=${confirmPrompt} | ||||
|         ?escapeKeyAction=${confirmPrompt} | ||||
|         @closed=${this._dialogClosed} | ||||
|         defaultAction="ignore" | ||||
|         .heading=${this._params.title | ||||
| @@ -140,10 +140,10 @@ class DialogBox extends LitElement { | ||||
|   } | ||||
|  | ||||
|   private _dialogClosed(ev) { | ||||
|     if (ev.detail.action === "ignore") { | ||||
|     if (this._params?.prompt && ev.detail.action === "ignore") { | ||||
|       return; | ||||
|     } | ||||
|     this.closeDialog(); | ||||
|     this._dismiss(); | ||||
|   } | ||||
|  | ||||
|   private _close(): void { | ||||
|   | ||||
| @@ -1,31 +1,31 @@ | ||||
| import { TemplateResult } from "lit-html"; | ||||
| import { fireEvent } from "../../common/dom/fire_event"; | ||||
|  | ||||
| interface BaseDialogParams { | ||||
| interface BaseDialogBoxParams { | ||||
|   confirmText?: string; | ||||
|   text?: string | TemplateResult; | ||||
|   title?: string; | ||||
|   warning?: boolean; | ||||
| } | ||||
|  | ||||
| export interface AlertDialogParams extends BaseDialogParams { | ||||
| export interface AlertDialogParams extends BaseDialogBoxParams { | ||||
|   confirm?: () => void; | ||||
| } | ||||
|  | ||||
| export interface ConfirmationDialogParams extends BaseDialogParams { | ||||
| export interface ConfirmationDialogParams extends BaseDialogBoxParams { | ||||
|   dismissText?: string; | ||||
|   confirm?: () => void; | ||||
|   cancel?: () => void; | ||||
| } | ||||
|  | ||||
| export interface PromptDialogParams extends BaseDialogParams { | ||||
| export interface PromptDialogParams extends BaseDialogBoxParams { | ||||
|   inputLabel?: string; | ||||
|   inputType?: string; | ||||
|   defaultValue?: string; | ||||
|   confirm?: (out?: string) => void; | ||||
| } | ||||
|  | ||||
| export interface DialogParams | ||||
| export interface DialogBoxParams | ||||
|   extends ConfirmationDialogParams, | ||||
|     PromptDialogParams { | ||||
|   confirm?: (out?: string) => void; | ||||
| @@ -37,10 +37,10 @@ export const loadGenericDialog = () => import("./dialog-box"); | ||||
|  | ||||
| const showDialogHelper = ( | ||||
|   element: HTMLElement, | ||||
|   dialogParams: DialogParams, | ||||
|   dialogParams: DialogBoxParams, | ||||
|   extra?: { | ||||
|     confirmation?: DialogParams["confirmation"]; | ||||
|     prompt?: DialogParams["prompt"]; | ||||
|     confirmation?: DialogBoxParams["confirmation"]; | ||||
|     prompt?: DialogBoxParams["prompt"]; | ||||
|   } | ||||
| ) => | ||||
|   new Promise((resolve) => { | ||||
|   | ||||
| @@ -44,7 +44,7 @@ class MoreInfoSun extends LitElement { | ||||
|               > | ||||
|               <ha-relative-time | ||||
|                 .hass=${this.hass} | ||||
|                 .datetimeObj=${item === "ris" ? risingDate : settingDate} | ||||
|                 .datetime=${item === "ris" ? risingDate : settingDate} | ||||
|               ></ha-relative-time> | ||||
|             </div> | ||||
|             <div class="value"> | ||||
|   | ||||
| @@ -60,6 +60,12 @@ export class HaTabsSubpageDataTable extends LitElement { | ||||
|    */ | ||||
|   @property({ type: Boolean }) public hasFab = false; | ||||
|  | ||||
|   /** | ||||
|    * Add an extra rows at the bottom of the datatabel | ||||
|    * @type {TemplateResult} | ||||
|    */ | ||||
|   @property({ attribute: false }) public appendRow?; | ||||
|  | ||||
|   /** | ||||
|    * Field with a unique id per entry in data. | ||||
|    * @type {String} | ||||
| @@ -171,6 +177,7 @@ export class HaTabsSubpageDataTable extends LitElement { | ||||
|           .noDataText=${this.noDataText} | ||||
|           .dir=${computeRTLDirection(this.hass)} | ||||
|           .clickable=${this.clickable} | ||||
|           .appendRow=${this.appendRow} | ||||
|         > | ||||
|           ${!this.narrow | ||||
|             ? html` | ||||
|   | ||||
| @@ -17,6 +17,7 @@ import { PolymerChangedEvent } from "../../../polymer-types"; | ||||
| import { haStyleDialog } from "../../../resources/styles"; | ||||
| import { HomeAssistant } from "../../../types"; | ||||
| import { AreaRegistryDetailDialogParams } from "./show-dialog-area-registry-detail"; | ||||
| import { navigate } from "../../../common/navigate"; | ||||
|  | ||||
| class DialogAreaDetail extends LitElement { | ||||
|   @property({ attribute: false }) public hass!: HomeAssistant; | ||||
| @@ -154,6 +155,8 @@ class DialogAreaDetail extends LitElement { | ||||
|     } finally { | ||||
|       this._submitting = false; | ||||
|     } | ||||
|  | ||||
|     navigate(this, "/config/areas/dashboard"); | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResult[] { | ||||
|   | ||||
| @@ -39,7 +39,7 @@ export class HaWaitForTriggerAction extends LitElement | ||||
|         )} | ||||
|       > | ||||
|         <ha-switch | ||||
|           .checked=${continue_on_timeout} | ||||
|           .checked=${continue_on_timeout ?? true} | ||||
|           @change=${this._continueChanged} | ||||
|         ></ha-switch> | ||||
|       </ha-formfield> | ||||
|   | ||||
| @@ -18,13 +18,9 @@ import "@polymer/paper-input/paper-textarea"; | ||||
| import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light"; | ||||
| import "../../../components/entity/ha-entity-toggle"; | ||||
| import "@material/mwc-button/mwc-button"; | ||||
| import "./trigger/ha-automation-trigger"; | ||||
| import "./condition/ha-automation-condition"; | ||||
| import "./action/ha-automation-action"; | ||||
| import { fireEvent } from "../../../common/dom/fire_event"; | ||||
| import { haStyle } from "../../../resources/styles"; | ||||
| import { HassEntity } from "home-assistant-js-websocket"; | ||||
| import { navigate } from "../../../common/navigate"; | ||||
| import { | ||||
|   BlueprintOrError, | ||||
|   Blueprints, | ||||
| @@ -63,7 +59,7 @@ export class HaBlueprintAutomationEditor extends LitElement { | ||||
|  | ||||
|   protected render() { | ||||
|     const blueprint = this._blueprint; | ||||
|     return html`<ha-config-section .isWide=${this.isWide}> | ||||
|     return html`<ha-config-section vertical .isWide=${this.isWide}> | ||||
|         ${!this.narrow | ||||
|           ? html` <span slot="header">${this.config.alias}</span> ` | ||||
|           : ""} | ||||
| @@ -119,7 +115,7 @@ export class HaBlueprintAutomationEditor extends LitElement { | ||||
|         </ha-card> | ||||
|       </ha-config-section> | ||||
|  | ||||
|       <ha-config-section .isWide=${this.isWide}> | ||||
|       <ha-config-section vertical .isWide=${this.isWide}> | ||||
|         <span slot="header" | ||||
|           >${this.hass.localize( | ||||
|             "ui.panel.config.automation.editor.blueprint.header" | ||||
| @@ -144,11 +140,6 @@ export class HaBlueprintAutomationEditor extends LitElement { | ||||
|                     "ui.panel.config.automation.editor.blueprint.no_blueprints" | ||||
|                   ) | ||||
|               : html`<ha-circular-progress active></ha-circular-progress>`} | ||||
|             <mwc-button @click=${this._navigateBlueprints}> | ||||
|               ${this.hass.localize( | ||||
|                 "ui.panel.config.automation.editor.blueprint.manage_blueprints" | ||||
|               )} | ||||
|             </mwc-button> | ||||
|           </div> | ||||
|  | ||||
|           ${this.config.use_blueprint.path | ||||
| @@ -157,41 +148,37 @@ export class HaBlueprintAutomationEditor extends LitElement { | ||||
|                   There is an error in this Blueprint: ${blueprint.error} | ||||
|                 </p>` | ||||
|               : html`${blueprint?.metadata.description | ||||
|                   ? html`<p>${blueprint.metadata.description}</p>` | ||||
|                   ? html`<p class="card-content pre-line"> | ||||
|                       ${blueprint.metadata.description} | ||||
|                     </p>` | ||||
|                   : ""} | ||||
|                 ${blueprint?.metadata?.input && | ||||
|                 Object.keys(blueprint.metadata.input).length | ||||
|                   ? html`<h3> | ||||
|                         ${this.hass.localize( | ||||
|                           "ui.panel.config.automation.editor.blueprint.inputs" | ||||
|                         )} | ||||
|                       </h3> | ||||
|                       ${Object.entries(blueprint.metadata.input).map( | ||||
|                         ([key, value]) => | ||||
|                           html`<ha-settings-row .narrow=${this.narrow}> | ||||
|                             <span slot="heading">${value?.name || key}</span> | ||||
|                             <span slot="description" | ||||
|                               >${value?.description}</span | ||||
|                             > | ||||
|                             ${value?.selector | ||||
|                               ? html`<ha-selector | ||||
|                                   .hass=${this.hass} | ||||
|                                   .selector=${value.selector} | ||||
|                                   .key=${key} | ||||
|                                   .value=${(this.config.use_blueprint.input && | ||||
|                                     this.config.use_blueprint.input[key]) || | ||||
|                                   value?.default} | ||||
|                                   @value-changed=${this._inputChanged} | ||||
|                                 ></ha-selector>` | ||||
|                               : html`<paper-input | ||||
|                                   .key=${key} | ||||
|                                   .value=${this.config.use_blueprint.input && | ||||
|                                   this.config.use_blueprint.input[key]} | ||||
|                                   @value-changed=${this._inputChanged} | ||||
|                                   no-label-float | ||||
|                                 ></paper-input>`} | ||||
|                           </ha-settings-row>` | ||||
|                       )}` | ||||
|                   ? Object.entries(blueprint.metadata.input).map( | ||||
|                       ([key, value]) => | ||||
|                         html`<ha-settings-row .narrow=${this.narrow}> | ||||
|                           <span slot="heading">${value?.name || key}</span> | ||||
|                           <span slot="description">${value?.description}</span> | ||||
|                           ${value?.selector | ||||
|                             ? html`<ha-selector | ||||
|                                 .hass=${this.hass} | ||||
|                                 .selector=${value.selector} | ||||
|                                 .key=${key} | ||||
|                                 .value=${(this.config.use_blueprint.input && | ||||
|                                   this.config.use_blueprint.input[key]) || | ||||
|                                 value?.default} | ||||
|                                 @value-changed=${this._inputChanged} | ||||
|                               ></ha-selector>` | ||||
|                             : html`<paper-input | ||||
|                                 .key=${key} | ||||
|                                 required | ||||
|                                 .value=${this.config.use_blueprint.input && | ||||
|                                 this.config.use_blueprint.input[key]} | ||||
|                                 @value-changed=${this._inputChanged} | ||||
|                                 no-label-float | ||||
|                               ></paper-input>`} | ||||
|                         </ha-settings-row>` | ||||
|                     ) | ||||
|                   : html`<p class="padding"> | ||||
|                       ${this.hass.localize( | ||||
|                         "ui.panel.config.automation.editor.blueprint.no_inputs" | ||||
| @@ -237,12 +224,18 @@ export class HaBlueprintAutomationEditor extends LitElement { | ||||
|     ) { | ||||
|       return; | ||||
|     } | ||||
|     const input = { ...this.config.use_blueprint.input, [key]: value }; | ||||
|  | ||||
|     if (value === "" || value === undefined) { | ||||
|       delete input[key]; | ||||
|     } | ||||
|  | ||||
|     fireEvent(this, "value-changed", { | ||||
|       value: { | ||||
|         ...this.config!, | ||||
|         use_blueprint: { | ||||
|           ...this.config.use_blueprint, | ||||
|           input: { ...this.config.use_blueprint.input, [key]: value }, | ||||
|           input, | ||||
|         }, | ||||
|       }, | ||||
|     }); | ||||
| @@ -267,25 +260,18 @@ export class HaBlueprintAutomationEditor extends LitElement { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   private _navigateBlueprints() { | ||||
|     navigate(this, "/config/blueprint"); | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResult[] { | ||||
|     return [ | ||||
|       haStyle, | ||||
|       css` | ||||
|         ha-card { | ||||
|           overflow: hidden; | ||||
|         } | ||||
|         .padding { | ||||
|           padding: 16px; | ||||
|         } | ||||
|         .pre-line { | ||||
|           white-space: pre-line; | ||||
|         } | ||||
|         .blueprint-picker-container { | ||||
|           padding: 16px; | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|           justify-content: space-between; | ||||
|         } | ||||
|         h3 { | ||||
|           margin: 16px; | ||||
| @@ -304,10 +290,10 @@ export class HaBlueprintAutomationEditor extends LitElement { | ||||
|           border-top: 1px solid var(--divider-color); | ||||
|         } | ||||
|         :host(:not([narrow])) ha-settings-row paper-input { | ||||
|           width: 50%; | ||||
|           width: 60%; | ||||
|         } | ||||
|         :host(:not([narrow])) ha-settings-row ha-selector { | ||||
|           width: 50%; | ||||
|           width: 60%; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   | ||||
| @@ -32,6 +32,7 @@ import "../../../components/ha-svg-icon"; | ||||
| import "../../../components/ha-yaml-editor"; | ||||
| import { showToast } from "../../../util/toast"; | ||||
| import type { HaYamlEditor } from "../../../components/ha-yaml-editor"; | ||||
| import { copyToClipboard } from "../../../common/util/copy-clipboard"; | ||||
| import { | ||||
|   AutomationConfig, | ||||
|   AutomationEntity, | ||||
| @@ -206,6 +207,7 @@ 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} | ||||
| @@ -213,6 +215,7 @@ 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} | ||||
| @@ -394,7 +397,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { | ||||
|  | ||||
|   private async _copyYaml() { | ||||
|     if (this._editor?.yaml) { | ||||
|       navigator.clipboard.writeText(this._editor.yaml); | ||||
|       copyToClipboard(this._editor.yaml); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import { | ||||
|   internalProperty, | ||||
|   query, | ||||
|   TemplateResult, | ||||
|   css, | ||||
| } from "lit-element"; | ||||
| import "../../../components/ha-dialog"; | ||||
| import { haStyleDialog } from "../../../resources/styles"; | ||||
| @@ -73,7 +74,9 @@ class DialogImportBlueprint extends LitElement { | ||||
|                   this._result.blueprint.metadata.domain | ||||
|                 )} | ||||
|                 <br /> | ||||
|                 ${this._result.blueprint.metadata.description} | ||||
|                 <p class="pre-line"> | ||||
|                   ${this._result.blueprint.metadata.description} | ||||
|                 </p> | ||||
|                 ${this._result.validation_errors | ||||
|                   ? html` | ||||
|                       <p class="error"> | ||||
| @@ -104,7 +107,16 @@ class DialogImportBlueprint extends LitElement { | ||||
|                   <pre>${this._result.raw_data}</pre> | ||||
|                 </ha-expansion-panel>` | ||||
|             : html`${this.hass.localize( | ||||
|                   "ui.panel.config.blueprint.add.import_introduction" | ||||
|                   "ui.panel.config.blueprint.add.import_introduction_link", | ||||
|                   "community_link", | ||||
|                   html`<a | ||||
|                     href="https://www.home-assistant.io/get-blueprints" | ||||
|                     target="_blank" | ||||
|                     rel="noreferrer noopener" | ||||
|                     >${this.hass.localize( | ||||
|                       "ui.panel.config.blueprint.add.community_forums" | ||||
|                     )}</a | ||||
|                   >` | ||||
|                 )}<paper-input | ||||
|                   id="input" | ||||
|                   .label=${this.hass.localize( | ||||
| @@ -199,8 +211,15 @@ class DialogImportBlueprint extends LitElement { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResult { | ||||
|     return haStyleDialog; | ||||
|   static get styles(): CSSResult[] { | ||||
|     return [ | ||||
|       haStyleDialog, | ||||
|       css` | ||||
|         .pre-line { | ||||
|           white-space: pre-line; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import "../../../components/ha-fab"; | ||||
| import "@material/mwc-icon-button"; | ||||
| import { mdiPlus, mdiHelpCircle, mdiDelete, mdiRobot } from "@mdi/js"; | ||||
| import { mdiHelpCircle, mdiDelete, mdiRobot, mdiDownload } from "@mdi/js"; | ||||
| import "@polymer/paper-tooltip/paper-tooltip"; | ||||
| import { | ||||
|   CSSResult, | ||||
| @@ -112,7 +112,6 @@ class HaBlueprintOverview extends LitElement { | ||||
|       create: { | ||||
|         title: "", | ||||
|         type: narrow ? "icon-button" : undefined, | ||||
|         width: narrow ? undefined : "180px", | ||||
|         template: (_, blueprint: any) => | ||||
|           blueprint.error | ||||
|             ? "" | ||||
| @@ -126,8 +125,9 @@ class HaBlueprintOverview extends LitElement { | ||||
|                   "ui.panel.config.blueprint.overview.use_blueprint" | ||||
|                 )} | ||||
|                 @click=${(ev) => this._createNew(ev)} | ||||
|                 ><ha-svg-icon .path=${mdiRobot}></ha-svg-icon | ||||
|               ></mwc-icon-button>` | ||||
|               > | ||||
|                 <ha-svg-icon .path=${mdiRobot}></ha-svg-icon> | ||||
|               </mwc-icon-button>` | ||||
|             : html`<mwc-button | ||||
|                 .blueprint=${blueprint} | ||||
|                 @click=${(ev) => this._createNew(ev)} | ||||
| @@ -170,6 +170,23 @@ class HaBlueprintOverview extends LitElement { | ||||
|           "ui.panel.config.blueprint.overview.no_blueprints" | ||||
|         )} | ||||
|         hasFab | ||||
|         .appendRow=${html` <div | ||||
|           class="mdc-data-table__cell" | ||||
|           style="width: 100%; text-align: center;" | ||||
|           role="cell" | ||||
|         > | ||||
|           <a | ||||
|             href="https://www.home-assistant.io/get-blueprints" | ||||
|             target="_blank" | ||||
|             rel="noreferrer noopener" | ||||
|           > | ||||
|             <mwc-button | ||||
|               >${this.hass.localize( | ||||
|                 "ui.panel.config.blueprint.overview.discover_more" | ||||
|               )}</mwc-button | ||||
|             > | ||||
|           </a> | ||||
|         </div>`} | ||||
|       > | ||||
|         <mwc-icon-button slot="toolbar-icon" @click=${this._showHelp}> | ||||
|           <ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon> | ||||
| @@ -182,7 +199,7 @@ class HaBlueprintOverview extends LitElement { | ||||
|           extended | ||||
|           @click=${this._addBlueprint} | ||||
|         > | ||||
|           <ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon> | ||||
|           <ha-svg-icon slot="icon" .path=${mdiDownload}></ha-svg-icon> | ||||
|         </ha-fab> | ||||
|       </hass-tabs-subpage-data-table> | ||||
|     `; | ||||
| @@ -195,7 +212,10 @@ class HaBlueprintOverview extends LitElement { | ||||
|         ${this.hass.localize("ui.panel.config.blueprint.overview.introduction")} | ||||
|         <p> | ||||
|           <a | ||||
|             href="${documentationUrl(this.hass, "/docs/blueprint/editor/")}" | ||||
|             href="${documentationUrl( | ||||
|               this.hass, | ||||
|               "/docs/automation/using_blueprints/" | ||||
|             )}" | ||||
|             target="_blank" | ||||
|             rel="noreferrer" | ||||
|           > | ||||
|   | ||||
| @@ -8,7 +8,6 @@ import { | ||||
|   html, | ||||
|   LitElement, | ||||
|   property, | ||||
|   internalProperty, | ||||
|   PropertyValues, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| @@ -31,7 +30,7 @@ export class HaDeviceEntitiesCard extends LitElement { | ||||
|  | ||||
|   @property() public entities!: EntityRegistryStateEntry[]; | ||||
|  | ||||
|   @internalProperty() private _showDisabled = false; | ||||
|   @property() public showDisabled = false; | ||||
|  | ||||
|   private _entityRows: Array<LovelaceRow | HuiErrorCard> = []; | ||||
|  | ||||
| @@ -68,7 +67,7 @@ export class HaDeviceEntitiesCard extends LitElement { | ||||
|                 })} | ||||
|               </div> | ||||
|               ${disabledEntities.length | ||||
|                 ? !this._showDisabled | ||||
|                 ? !this.showDisabled | ||||
|                   ? html` | ||||
|                       <button | ||||
|                         class="show-more" | ||||
| @@ -119,7 +118,7 @@ export class HaDeviceEntitiesCard extends LitElement { | ||||
|   } | ||||
|  | ||||
|   private _toggleShowDisabled() { | ||||
|     this._showDisabled = !this._showDisabled; | ||||
|     this.showDisabled = !this.showDisabled; | ||||
|   } | ||||
|  | ||||
|   private _renderEntity(entry: EntityRegistryStateEntry): TemplateResult { | ||||
| @@ -227,3 +226,9 @@ export class HaDeviceEntitiesCard extends LitElement { | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|     "ha-device-entities-card": HaDeviceEntitiesCard; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -19,10 +19,11 @@ 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 { haStyleDialog } from "../../../../resources/styles"; | ||||
| import { haStyle, haStyleDialog } from "../../../../resources/styles"; | ||||
|  | ||||
| @customElement("dialog-device-registry-detail") | ||||
| class DialogDeviceRegistryDetail extends LitElement { | ||||
| @@ -36,6 +37,8 @@ class DialogDeviceRegistryDetail extends LitElement { | ||||
|  | ||||
|   @internalProperty() private _areaId?: string; | ||||
|  | ||||
|   @internalProperty() private _disabledBy!: string | null; | ||||
|  | ||||
|   @internalProperty() private _submitting?: boolean; | ||||
|  | ||||
|   public async showDialog( | ||||
| @@ -45,6 +48,7 @@ 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; | ||||
|   } | ||||
|  | ||||
| @@ -80,6 +84,32 @@ 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 | ||||
| @@ -109,12 +139,17 @@ 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) { | ||||
| @@ -128,6 +163,7 @@ class DialogDeviceRegistryDetail extends LitElement { | ||||
|  | ||||
|   static get styles(): CSSResult[] { | ||||
|     return [ | ||||
|       haStyle, | ||||
|       haStyleDialog, | ||||
|       css` | ||||
|         .form { | ||||
| @@ -139,6 +175,15 @@ 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; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
|   | ||||
| @@ -46,6 +46,7 @@ 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; | ||||
| @@ -246,6 +247,28 @@ 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> | ||||
|  | ||||
| @@ -255,6 +278,7 @@ export class HaConfigDevicePage extends LitElement { | ||||
|                     <ha-device-entities-card | ||||
|                       .hass=${this.hass} | ||||
|                       .entities=${entities} | ||||
|                       .showDisabled=${device.disabled_by !== null} | ||||
|                     > | ||||
|                     </ha-device-entities-card> | ||||
|                   ` | ||||
| @@ -272,9 +296,14 @@ export class HaConfigDevicePage extends LitElement { | ||||
|                         )} | ||||
|                         <ha-icon-button | ||||
|                           @click=${this._showAutomationDialog} | ||||
|                           title=${this.hass.localize( | ||||
|                             "ui.panel.config.devices.automation.create" | ||||
|                           )} | ||||
|                           .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" | ||||
|                               )} | ||||
|                           icon="hass:plus-circle" | ||||
|                         ></ha-icon-button> | ||||
|                       </h1> | ||||
| @@ -342,9 +371,16 @@ export class HaConfigDevicePage extends LitElement { | ||||
|  | ||||
|                                   <ha-icon-button | ||||
|                                     @click=${this._createScene} | ||||
|                                     title=${this.hass.localize( | ||||
|                                       "ui.panel.config.devices.scene.create" | ||||
|                                     )} | ||||
|                                     .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" | ||||
|                                           ) | ||||
|                                     } | ||||
|                                     icon="hass:plus-circle" | ||||
|                                   ></ha-icon-button> | ||||
|                         </h1> | ||||
| @@ -415,9 +451,14 @@ export class HaConfigDevicePage extends LitElement { | ||||
|                           )} | ||||
|                           <ha-icon-button | ||||
|                             @click=${this._showScriptDialog} | ||||
|                             title=${this.hass.localize( | ||||
|                               "ui.panel.config.devices.script.create" | ||||
|                             )} | ||||
|                             .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" | ||||
|                                 )} | ||||
|                             icon="hass:plus-circle" | ||||
|                           ></ha-icon-button> | ||||
|                         </h1> | ||||
| @@ -632,128 +673,137 @@ export class HaConfigDevicePage extends LitElement { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResult { | ||||
|     return css` | ||||
|       .container { | ||||
|         display: flex; | ||||
|         flex-wrap: wrap; | ||||
|         margin: auto; | ||||
|         max-width: 1000px; | ||||
|         margin-top: 32px; | ||||
|         margin-bottom: 32px; | ||||
|       } | ||||
|   private async _enableDevice(): Promise<void> { | ||||
|     await updateDeviceRegistryEntry(this.hass, this.deviceId, { | ||||
|       disabled_by: null, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|       .card-header { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: space-between; | ||||
|       } | ||||
|   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 ha-icon-button { | ||||
|         margin-right: -8px; | ||||
|         color: var(--primary-color); | ||||
|         height: auto; | ||||
|       } | ||||
|         .card-header { | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|           justify-content: space-between; | ||||
|         } | ||||
|  | ||||
|       .device-info { | ||||
|         padding: 16px; | ||||
|       } | ||||
|         .card-header ha-icon-button { | ||||
|           margin-right: -8px; | ||||
|           color: var(--primary-color); | ||||
|           height: auto; | ||||
|         } | ||||
|  | ||||
|       .show-more { | ||||
|       } | ||||
|         .device-info { | ||||
|           padding: 16px; | ||||
|         } | ||||
|  | ||||
|       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); | ||||
|       } | ||||
|         .show-more { | ||||
|         } | ||||
|  | ||||
|       .header { | ||||
|         display: flex; | ||||
|         justify-content: space-between; | ||||
|       } | ||||
|         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); | ||||
|         } | ||||
|  | ||||
|       .column, | ||||
|       .fullwidth { | ||||
|         padding: 8px; | ||||
|         box-sizing: border-box; | ||||
|       } | ||||
|       .column { | ||||
|         width: 33%; | ||||
|         flex-grow: 1; | ||||
|       } | ||||
|       .fullwidth { | ||||
|         width: 100%; | ||||
|         flex-grow: 1; | ||||
|       } | ||||
|         .header { | ||||
|           display: flex; | ||||
|           justify-content: space-between; | ||||
|         } | ||||
|  | ||||
|       .header-right { | ||||
|         align-self: center; | ||||
|       } | ||||
|         .column, | ||||
|         .fullwidth { | ||||
|           padding: 8px; | ||||
|           box-sizing: border-box; | ||||
|         } | ||||
|         .column { | ||||
|           width: 33%; | ||||
|           flex-grow: 1; | ||||
|         } | ||||
|         .fullwidth { | ||||
|           width: 100%; | ||||
|           flex-grow: 1; | ||||
|         } | ||||
|  | ||||
|       .header-right img { | ||||
|         height: 30px; | ||||
|       } | ||||
|         .header-right { | ||||
|           align-self: center; | ||||
|         } | ||||
|  | ||||
|       .header-right { | ||||
|         display: flex; | ||||
|       } | ||||
|         .header-right img { | ||||
|           height: 30px; | ||||
|         } | ||||
|  | ||||
|       .header-right:first-child { | ||||
|         width: 100%; | ||||
|         justify-content: flex-end; | ||||
|       } | ||||
|         .header-right { | ||||
|           display: flex; | ||||
|         } | ||||
|  | ||||
|       .header-right > *:not(:first-child) { | ||||
|         margin-left: 16px; | ||||
|       } | ||||
|         .header-right:first-child { | ||||
|           width: 100%; | ||||
|           justify-content: flex-end; | ||||
|         } | ||||
|  | ||||
|       .battery { | ||||
|         align-self: center; | ||||
|         align-items: center; | ||||
|         display: flex; | ||||
|       } | ||||
|         .header-right > *:not(:first-child) { | ||||
|           margin-left: 16px; | ||||
|         } | ||||
|  | ||||
|       .column > *:not(:first-child) { | ||||
|         margin-top: 16px; | ||||
|       } | ||||
|         .battery { | ||||
|           align-self: center; | ||||
|           align-items: center; | ||||
|           display: flex; | ||||
|         } | ||||
|  | ||||
|       :host([narrow]) .column { | ||||
|         width: 100%; | ||||
|       } | ||||
|         .column > *:not(:first-child) { | ||||
|           margin-top: 16px; | ||||
|         } | ||||
|  | ||||
|       :host([narrow]) .container { | ||||
|         margin-top: 0; | ||||
|       } | ||||
|         :host([narrow]) .column { | ||||
|           width: 100%; | ||||
|         } | ||||
|  | ||||
|       paper-item { | ||||
|         cursor: pointer; | ||||
|         font-size: var(--paper-font-body1_-_font-size); | ||||
|       } | ||||
|         :host([narrow]) .container { | ||||
|           margin-top: 0; | ||||
|         } | ||||
|  | ||||
|       paper-item.no-link { | ||||
|         cursor: default; | ||||
|       } | ||||
|         paper-item { | ||||
|           cursor: pointer; | ||||
|           font-size: var(--paper-font-body1_-_font-size); | ||||
|         } | ||||
|  | ||||
|       a { | ||||
|         text-decoration: none; | ||||
|         color: var(--primary-color); | ||||
|       } | ||||
|         paper-item.no-link { | ||||
|           cursor: default; | ||||
|         } | ||||
|  | ||||
|       ha-card { | ||||
|         padding-bottom: 8px; | ||||
|       } | ||||
|         a { | ||||
|           text-decoration: none; | ||||
|           color: var(--primary-color); | ||||
|         } | ||||
|  | ||||
|       ha-card a { | ||||
|         color: var(--primary-text-color); | ||||
|       } | ||||
|     `; | ||||
|         ha-card { | ||||
|           padding-bottom: 8px; | ||||
|         } | ||||
|  | ||||
|         ha-card a { | ||||
|           color: var(--primary-text-color); | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,9 @@ | ||||
| import { mdiPlus } from "@mdi/js"; | ||||
| import { mdiPlus, mdiFilterVariant, mdiCancel } from "@mdi/js"; | ||||
| import "@material/mwc-list/mwc-list-item"; | ||||
| import "@polymer/paper-tooltip/paper-tooltip"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
| @@ -7,7 +11,9 @@ 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"; | ||||
| @@ -18,6 +24,7 @@ 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 { | ||||
| @@ -34,6 +41,7 @@ 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; | ||||
| @@ -64,6 +72,12 @@ export class HaConfigDeviceDashboard extends LitElement { | ||||
|     window.location.search | ||||
|   ); | ||||
|  | ||||
|   @internalProperty() private _showDisabled = false; | ||||
|  | ||||
|   @internalProperty() private _filter = ""; | ||||
|  | ||||
|   @internalProperty() private _numHiddenDevices = 0; | ||||
|  | ||||
|   private _activeFilters = memoizeOne( | ||||
|     ( | ||||
|       entries: ConfigEntry[], | ||||
| @@ -74,6 +88,10 @@ 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 | ||||
|             ); | ||||
| @@ -105,6 +123,7 @@ 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 | ||||
| @@ -117,6 +136,9 @@ 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) { | ||||
| @@ -145,6 +167,7 @@ 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); | ||||
| @@ -152,6 +175,10 @@ export class HaConfigDeviceDashboard extends LitElement { | ||||
|         } | ||||
|       }); | ||||
|  | ||||
|       if (!showDisabled) { | ||||
|         outputDevices = outputDevices.filter((device) => !device.disabled_by); | ||||
|       } | ||||
|  | ||||
|       outputDevices = outputDevices.map((device) => { | ||||
|         return { | ||||
|           ...device, | ||||
| @@ -182,16 +209,19 @@ export class HaConfigDeviceDashboard extends LitElement { | ||||
|         }; | ||||
|       }); | ||||
|  | ||||
|       this._numHiddenDevices = startLength - outputDevices.length; | ||||
|       return { devicesOutput: outputDevices, filteredDomains: filterDomains }; | ||||
|     } | ||||
|   ); | ||||
|  | ||||
|   private _columns = memoizeOne( | ||||
|     (narrow: boolean): DataTableColumnContainer => { | ||||
|     (narrow: boolean, showDisabled: boolean): DataTableColumnContainer => { | ||||
|       const columns: DataTableColumnContainer = narrow | ||||
|         ? { | ||||
|             name: { | ||||
|               title: "Device", | ||||
|               title: this.hass.localize( | ||||
|                 "ui.panel.config.devices.data_table.device" | ||||
|               ), | ||||
|               sortable: true, | ||||
|               filterable: true, | ||||
|               direction: "asc", | ||||
| @@ -277,6 +307,24 @@ 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; | ||||
|     } | ||||
|   ); | ||||
| @@ -298,9 +346,119 @@ 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 | ||||
| @@ -311,13 +469,9 @@ export class HaConfigDeviceDashboard extends LitElement { | ||||
|           : "/config"} | ||||
|         .tabs=${configSections.integrations} | ||||
|         .route=${this.route} | ||||
|         .columns=${this._columns(this.narrow)} | ||||
|         .columns=${this._columns(this.narrow, this._showDisabled)} | ||||
|         .data=${devicesOutput} | ||||
|         .activeFilters=${this._activeFilters( | ||||
|           this.entries, | ||||
|           this._searchParms, | ||||
|           this.hass.localize | ||||
|         )} | ||||
|         .filter=${this._filter} | ||||
|         @row-click=${this._handleRowClicked} | ||||
|         clickable | ||||
|         .hasFab=${includeZHAFab} | ||||
| @@ -333,6 +487,15 @@ 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> | ||||
|     `; | ||||
|   } | ||||
| @@ -363,6 +526,136 @@ 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 { | ||||
|   | ||||
| @@ -111,10 +111,19 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) { | ||||
|     return html` | ||||
|       ${!stateObj | ||||
|         ? html` | ||||
|             <div class="container"> | ||||
|             <div class="container warning"> | ||||
|               ${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> | ||||
|           ` | ||||
|         : ""} | ||||
| @@ -161,6 +170,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) { | ||||
|         <div class="row"> | ||||
|           <ha-switch | ||||
|             .checked=${!this._disabledBy} | ||||
|             .disabled=${this._device?.disabled_by} | ||||
|             @change=${this._disabledByChanged} | ||||
|           > | ||||
|           </ha-switch> | ||||
|   | ||||
| @@ -189,9 +189,11 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { | ||||
|           ? (name, entity: any) => | ||||
|               html` | ||||
|                 ${name}<br /> | ||||
|                 ${entity.entity_id} | | ||||
|                 ${this.hass.localize(`component.${entity.platform}.title`) || | ||||
|                 entity.platform} | ||||
|                 <div class="secondary"> | ||||
|                   ${entity.entity_id} | | ||||
|                   ${this.hass.localize(`component.${entity.platform}.title`) || | ||||
|                   entity.platform} | ||||
|                 </div> | ||||
|               ` | ||||
|           : undefined, | ||||
|       }, | ||||
|   | ||||
| @@ -5,6 +5,8 @@ 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 | ||||
| @@ -16,8 +18,8 @@ export class HaConfigSection extends LitElement { | ||||
|         <div | ||||
|           class="together layout ${classMap({ | ||||
|             narrow: !this.isWide, | ||||
|             vertical: !this.isWide, | ||||
|             horizontal: this.isWide, | ||||
|             vertical: this.vertical || !this.isWide, | ||||
|             horizontal: !this.vertical && this.isWide, | ||||
|           })}" | ||||
|         > | ||||
|           <div class="intro"><slot name="introduction"></slot></div> | ||||
|   | ||||
| @@ -148,7 +148,7 @@ export class HaConfigHelpers extends LitElement { | ||||
|         .narrow=${this.narrow} | ||||
|         back-path="/config" | ||||
|         .route=${this.route} | ||||
|         .tabs=${configSections.automation} | ||||
|         .tabs=${configSections.helpers} | ||||
|         .columns=${this._columns(this.narrow, this.hass.language)} | ||||
|         .data=${this._getItems(this._stateItems)} | ||||
|         @row-click=${this._openEditDialog} | ||||
|   | ||||
| @@ -68,6 +68,8 @@ class DialogPersonDetail extends LitElement { | ||||
|  | ||||
|   @internalProperty() private _submitting = false; | ||||
|  | ||||
|   @internalProperty() private _personExists = false; | ||||
|  | ||||
|   private _deviceTrackersAvailable = memoizeOne((hass) => { | ||||
|     return Object.keys(hass.states).some( | ||||
|       (entityId) => | ||||
| @@ -79,6 +81,7 @@ 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 || []; | ||||
| @@ -88,6 +91,7 @@ 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; | ||||
| @@ -398,6 +402,7 @@ class DialogPersonDetail extends LitElement { | ||||
|         await this._params!.updateEntry(values); | ||||
|       } else { | ||||
|         await this._params!.createEntry(values); | ||||
|         this._personExists = true; | ||||
|       } | ||||
|       this._params = undefined; | ||||
|     } catch (err) { | ||||
| @@ -422,6 +427,14 @@ 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; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -35,6 +35,7 @@ import "../../../components/ha-icon-input"; | ||||
| import "../../../components/ha-svg-icon"; | ||||
| import "../../../components/ha-yaml-editor"; | ||||
| import type { HaYamlEditor } from "../../../components/ha-yaml-editor"; | ||||
| import { copyToClipboard } from "../../../common/util/copy-clipboard"; | ||||
| import { | ||||
|   Action, | ||||
|   deleteScript, | ||||
| @@ -545,7 +546,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { | ||||
|  | ||||
|   private async _copyYaml() { | ||||
|     if (this._editor?.yaml) { | ||||
|       navigator.clipboard.writeText(this._editor.yaml); | ||||
|       copyToClipboard(this._editor.yaml); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -84,7 +84,7 @@ export class HaConfigTags extends SubscribeMixin(LitElement) { | ||||
|                 ${tag.last_scanned_datetime | ||||
|                   ? html`<ha-relative-time | ||||
|                       .hass=${this.hass} | ||||
|                       .datetimeObj=${tag.last_scanned_datetime} | ||||
|                       .datetime=${tag.last_scanned_datetime} | ||||
|                     ></ha-relative-time>` | ||||
|                   : this.hass.localize("ui.panel.config.tags.never_scanned")} | ||||
|               </div>` | ||||
| @@ -103,7 +103,7 @@ export class HaConfigTags extends SubscribeMixin(LitElement) { | ||||
|             ${last_scanned_datetime | ||||
|               ? html`<ha-relative-time | ||||
|                   .hass=${this.hass} | ||||
|                   .datetimeObj=${last_scanned_datetime} | ||||
|                   .datetime=${last_scanned_datetime} | ||||
|                 ></ha-relative-time>` | ||||
|               : this.hass.localize("ui.panel.config.tags.never_scanned")} | ||||
|           `, | ||||
|   | ||||
| @@ -241,7 +241,7 @@ export class DialogAddUser extends LitElement { | ||||
|       user = userResponse.user; | ||||
|     } catch (err) { | ||||
|       this._loading = false; | ||||
|       this._error = err.code; | ||||
|       this._error = err.message; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| @@ -255,7 +255,7 @@ export class DialogAddUser extends LitElement { | ||||
|     } catch (err) { | ||||
|       await deleteUser(this.hass, user.id); | ||||
|       this._loading = false; | ||||
|       this._error = err.code; | ||||
|       this._error = err.message; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,7 @@ 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"; | ||||
| @@ -37,6 +38,8 @@ class DialogUserDetail extends LitElement { | ||||
|  | ||||
|   @internalProperty() private _isAdmin?: boolean; | ||||
|  | ||||
|   @internalProperty() private _isActive?: boolean; | ||||
|  | ||||
|   @internalProperty() private _error?: string; | ||||
|  | ||||
|   @internalProperty() private _params?: UserDetailDialogParams; | ||||
| @@ -48,6 +51,7 @@ 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; | ||||
|   } | ||||
|  | ||||
| @@ -91,15 +95,6 @@ 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 | ||||
| @@ -110,17 +105,21 @@ class DialogUserDetail extends LitElement { | ||||
|                 "ui.panel.config.users.editor.name" | ||||
|               )}" | ||||
|             ></paper-input> | ||||
|             <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} | ||||
|             <div class="row"> | ||||
|               <ha-formfield | ||||
|                 .label=${this.hass.localize( | ||||
|                   "ui.panel.config.users.editor.admin" | ||||
|                 )} | ||||
|                 .dir=${computeRTLDirection(this.hass)} | ||||
|               > | ||||
|               </ha-switch> | ||||
|             </ha-formfield> | ||||
|                 <ha-switch | ||||
|                   .disabled=${user.system_generated || user.is_owner} | ||||
|                   .checked=${this._isAdmin} | ||||
|                   @change=${this._adminChanged} | ||||
|                 > | ||||
|                 </ha-switch> | ||||
|               </ha-formfield> | ||||
|             </div> | ||||
|             ${!this._isAdmin | ||||
|               ? html` | ||||
|                   <br /> | ||||
| @@ -129,6 +128,27 @@ 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> | ||||
|  | ||||
| @@ -192,11 +212,16 @@ 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, | ||||
|         ], | ||||
| @@ -293,8 +318,13 @@ class DialogUserDetail extends LitElement { | ||||
|         .state:not(:first-child) { | ||||
|           margin-left: 8px; | ||||
|         } | ||||
|         ha-switch { | ||||
|           margin-top: 8px; | ||||
|         .row { | ||||
|           display: flex; | ||||
|           padding: 8px 0; | ||||
|         } | ||||
|         ha-help-tooltip { | ||||
|           margin-left: 4px; | ||||
|           position: relative; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   | ||||
| @@ -46,10 +46,17 @@ export class HaConfigUsers extends LitElement { | ||||
|           width: "25%", | ||||
|           direction: "asc", | ||||
|           grows: true, | ||||
|           template: (name) => html` | ||||
|             ${name || | ||||
|             this.hass!.localize("ui.panel.config.users.editor.unnamed_user")} | ||||
|           `, | ||||
|           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" | ||||
|                 )}`, | ||||
|         }, | ||||
|         username: { | ||||
|           title: this.hass.localize( | ||||
| @@ -59,6 +66,7 @@ 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")} | ||||
| @@ -71,13 +79,24 @@ export class HaConfigUsers extends LitElement { | ||||
|           sortable: true, | ||||
|           filterable: true, | ||||
|           width: "20%", | ||||
|           direction: "asc", | ||||
|           hidden: narrow, | ||||
|           template: (groupIds) => html` | ||||
|             ${this.hass.localize(`groups.${groupIds[0]}`)} | ||||
|           `, | ||||
|         }, | ||||
|       }; | ||||
|       if (!narrow) { | ||||
|         columns.system_generated = { | ||||
|         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: { | ||||
|           title: this.hass.localize( | ||||
|             "ui.panel.config.users.picker.headers.system" | ||||
|           ), | ||||
| @@ -87,8 +106,9 @@ export class HaConfigUsers extends LitElement { | ||||
|           width: "160px", | ||||
|           template: (generated) => | ||||
|             generated ? html`<ha-icon icon="hass:check"> </ha-icon>` : "", | ||||
|         }; | ||||
|       } | ||||
|         }, | ||||
|       }; | ||||
|  | ||||
|       return columns; | ||||
|     } | ||||
|   ); | ||||
|   | ||||
| @@ -84,9 +84,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { | ||||
|   } | ||||
|  | ||||
|   public setConfig(config: ButtonCardConfig): void { | ||||
|     if (!config.entity) { | ||||
|       throw new Error("Entity must be specified"); | ||||
|     } | ||||
|     if (config.entity && !isValidEntityId(config.entity)) { | ||||
|       throw new Error("Invalid entity"); | ||||
|     } | ||||
|   | ||||
| @@ -109,7 +109,7 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard { | ||||
|   } | ||||
|  | ||||
|   public setConfig(config: EntitiesCardConfig): void { | ||||
|     if (!config || !config.entities.length) { | ||||
|     if (!config.entities || !Array.isArray(config.entities)) { | ||||
|       throw new Error("Entities must be specified"); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,14 @@ import { customElement } from "lit-element"; | ||||
| import { HuiButtonCard } from "./hui-button-card"; | ||||
|  | ||||
| @customElement("hui-entity-button-card") | ||||
| class HuiEntityButtonCard extends HuiButtonCard {} | ||||
| class HuiEntityButtonCard extends HuiButtonCard { | ||||
|   public setConfig(config): void { | ||||
|     if (!config.entity) { | ||||
|       throw new Error("Entity must be specified"); | ||||
|     } | ||||
|     super.setConfig(config); | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   interface HTMLElementTagNameMap { | ||||
|   | ||||
| @@ -1,16 +1,16 @@ | ||||
| import { safeDump } from "js-yaml"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
|   LitElement, | ||||
|   internalProperty, | ||||
|   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,7 +49,6 @@ 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); | ||||
|   | ||||
| @@ -16,7 +16,6 @@ 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"; | ||||
| @@ -30,22 +29,9 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard { | ||||
|     return document.createElement("hui-history-graph-card-editor"); | ||||
|   } | ||||
|  | ||||
|   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 }; | ||||
|   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"] }; | ||||
|   } | ||||
|  | ||||
|   @property({ attribute: false }) public hass?: HomeAssistant; | ||||
| @@ -71,12 +57,12 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard { | ||||
|   } | ||||
|  | ||||
|   public setConfig(config: HistoryGraphCardConfig): void { | ||||
|     if (!config.entities.length) { | ||||
|       throw new Error("Entities must be specified"); | ||||
|     if (!config.entities || !Array.isArray(config.entities)) { | ||||
|       throw new Error("Entities need to be an array"); | ||||
|     } | ||||
|  | ||||
|     if (config.entities && !Array.isArray(config.entities)) { | ||||
|       throw new Error("Entities need to be an array"); | ||||
|     if (!config.entities.length) { | ||||
|       throw new Error("You must include at least one entity"); | ||||
|     } | ||||
|  | ||||
|     this._config = config; | ||||
|   | ||||
| @@ -29,7 +29,7 @@ class HuiHorizontalStackCard extends HuiStackCard { | ||||
|         } | ||||
|         #root > * { | ||||
|           flex: 1 1 0; | ||||
|           margin: 0 4px; | ||||
|           margin: var(--horizontal-stack-card-margin, var(--stack-card-margin, 0 4px)); | ||||
|           min-width: 0; | ||||
|         } | ||||
|         #root > *:first-child { | ||||
|   | ||||
| @@ -246,78 +246,73 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard { | ||||
|           ${!isUnavailable && | ||||
|           (mediaDescription || stateObj.attributes.media_title || showControls) | ||||
|             ? html` | ||||
|                 <div | ||||
|                   class="title-controls" | ||||
|                   style=${styleMap({ | ||||
|                     paddingRight: isOffState | ||||
|                       ? "0" | ||||
|                       : `${this._cardHeight - 40}px`, | ||||
|                   })} | ||||
|                 > | ||||
|                   ${!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" | ||||
|                 <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.browse_media" | ||||
|                                     `ui.card.media_player.${control.action}` | ||||
|                                   )} | ||||
|                                   @click=${this._handleBrowseMedia} | ||||
|                                   ><ha-svg-icon | ||||
|                                     .path=${mdiPlayBoxMultiple} | ||||
|                                   ></ha-svg-icon | ||||
|                                 ></mwc-icon-button> | ||||
|                                   .icon=${control.icon} | ||||
|                                   action=${control.action} | ||||
|                                   @click=${this._handleClick} | ||||
|                                 ></ha-icon-button> | ||||
|                               ` | ||||
|                             : ""} | ||||
|                         </div> | ||||
|                             )} | ||||
|                             ${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> | ||||
|                 ${!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> | ||||
| @@ -635,6 +630,11 @@ 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: 0; | ||||
|         right: 4px; | ||||
|         --mdc-icon-size: 24px; | ||||
|       } | ||||
|  | ||||
| @@ -693,7 +693,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard { | ||||
|       .more-info { | ||||
|         position: absolute; | ||||
|         top: 4px; | ||||
|         right: 0px; | ||||
|         right: 4px; | ||||
|       } | ||||
|  | ||||
|       .media-info { | ||||
|   | ||||
| @@ -29,7 +29,7 @@ class HuiVerticalStackCard extends HuiStackCard { | ||||
|           height: 100%; | ||||
|         } | ||||
|         #root > * { | ||||
|           margin: 4px 0 4px 0; | ||||
|           margin: var(--vertical-stack-card-margin, var(--stack-card-margin, 4px 0)); | ||||
|         } | ||||
|         #root > *:first-child { | ||||
|           margin-top: 0; | ||||
|   | ||||
| @@ -179,9 +179,6 @@ export class HuiActionEditor extends LitElement { | ||||
|       .dropdown { | ||||
|         display: flex; | ||||
|       } | ||||
|       paper-dropdown-menu { | ||||
|         flex-grow: 1; | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| import "@material/mwc-button"; | ||||
| import "@material/mwc-list/mwc-list-item"; | ||||
| import "@material/mwc-icon-button"; | ||||
| import "../../../components/ha-button-menu"; | ||||
| import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; | ||||
| import "@material/mwc-list/mwc-list-item"; | ||||
| import { mdiArrowDown, mdiArrowUp, mdiDotsVertical } from "@mdi/js"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResult, | ||||
| @@ -9,21 +10,20 @@ import { | ||||
|   html, | ||||
|   LitElement, | ||||
|   property, | ||||
|   TemplateResult, | ||||
|   queryAssignedNodes, | ||||
|   TemplateResult, | ||||
| } from "lit-element"; | ||||
| import { HomeAssistant } from "../../../types"; | ||||
| import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; | ||||
| import { swapCard, moveCard, addCard, deleteCard } from "../editor/config-util"; | ||||
| import { confDeleteCard } from "../editor/delete-card"; | ||||
| import { Lovelace, LovelaceCard } from "../types"; | ||||
| import { computeCardSize } from "../common/compute-card-size"; | ||||
| import { mdiDotsVertical, mdiArrowDown, mdiArrowUp } from "@mdi/js"; | ||||
| import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; | ||||
| import { showSelectViewDialog } from "../editor/select-view/show-select-view-dialog"; | ||||
| import { fireEvent } from "../../../common/dom/fire_event"; | ||||
| import "../../../components/ha-button-menu"; | ||||
| import { saveConfig } from "../../../data/lovelace"; | ||||
| import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; | ||||
| import { HomeAssistant } from "../../../types"; | ||||
| import { showSaveSuccessToast } from "../../../util/toast-saved-success"; | ||||
| import { computeCardSize } from "../common/compute-card-size"; | ||||
| import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; | ||||
| import { addCard, deleteCard, moveCard, swapCard } from "../editor/config-util"; | ||||
| import { showSelectViewDialog } from "../editor/select-view/show-select-view-dialog"; | ||||
| import { Lovelace, LovelaceCard } from "../types"; | ||||
|  | ||||
| @customElement("hui-card-options") | ||||
| export class HuiCardOptions extends LitElement { | ||||
| @@ -168,11 +168,7 @@ export class HuiCardOptions extends LitElement { | ||||
|   } | ||||
|  | ||||
|   private _editCard(): void { | ||||
|     showEditCardDialog(this, { | ||||
|       lovelaceConfig: this.lovelace!.config, | ||||
|       saveConfig: this.lovelace!.saveConfig, | ||||
|       path: this.path!, | ||||
|     }); | ||||
|     fireEvent(this, "ll-edit-card", { path: this.path! }); | ||||
|   } | ||||
|  | ||||
|   private _cardUp(): void { | ||||
| @@ -229,7 +225,7 @@ export class HuiCardOptions extends LitElement { | ||||
|   } | ||||
|  | ||||
|   private _deleteCard(): void { | ||||
|     confDeleteCard(this, this.hass!, this.lovelace!, this.path!); | ||||
|     fireEvent(this, "ll-delete-card", { path: this.path! }); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { mdiDragVerticalVariant } from "@mdi/js"; | ||||
| import { mdiDrag } 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=${mdiDragVerticalVariant}></ha-svg-icon> | ||||
|                     <ha-svg-icon .path=${mdiDrag}></ha-svg-icon> | ||||
|                     <ha-entity-picker | ||||
|                       .hass=${this.hass} | ||||
|                       .value=${entityConf.entity} | ||||
| @@ -192,7 +192,6 @@ 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; | ||||
|   | ||||
| @@ -72,7 +72,7 @@ class HuiMarquee extends LitElement { | ||||
|         display: flex; | ||||
|         position: relative; | ||||
|         align-items: center; | ||||
|         height: 1em; | ||||
|         height: 1.2em; | ||||
|         contain: strict; | ||||
|       } | ||||
|  | ||||
|   | ||||
| @@ -26,7 +26,11 @@ 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.generic.theme" | ||||
|         )} (${this.hass!.localize( | ||||
|           "ui.panel.lovelace.editor.card.config.optional" | ||||
|         )})`} | ||||
|         dynamic-align | ||||
|       > | ||||
|         <paper-listbox | ||||
| @@ -55,6 +59,9 @@ export class HuiThemeSelectEditor extends LitElement { | ||||
|       paper-dropdown-menu { | ||||
|         width: 100%; | ||||
|       } | ||||
|       paper-item { | ||||
|         cursor: pointer; | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -353,11 +353,9 @@ export class HuiCardPicker extends LitElement { | ||||
|           max-width: 500px; | ||||
|           display: flex; | ||||
|           flex-direction: column; | ||||
|           border-radius: 4px; | ||||
|           border: 1px solid var(--divider-color); | ||||
|           border-radius: var(--ha-card-border-radius, 4px); | ||||
|           background: var(--primary-background-color, #fafafa); | ||||
|           cursor: pointer; | ||||
|           box-sizing: border-box; | ||||
|           position: relative; | ||||
|         } | ||||
|  | ||||
| @@ -375,7 +373,6 @@ 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); | ||||
|         } | ||||
|  | ||||
| @@ -408,6 +405,10 @@ 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 { | ||||
|   | ||||
| @@ -1,14 +1,5 @@ | ||||
| import { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item"; | ||||
| import { | ||||
|   mdiCheckboxBlankOutline, | ||||
|   mdiCheckBoxOutline, | ||||
|   mdiCodeBracesBox, | ||||
|   mdiDotsVertical, | ||||
|   mdiFormSelect, | ||||
|   mdiHelpCircleOutline, | ||||
| } from "@mdi/js"; | ||||
| import { mdiHelpCircle } from "@mdi/js"; | ||||
| import deepFreeze from "deep-freeze"; | ||||
| import { UnsubscribeFunc } from "home-assistant-js-websocket"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResultArray, | ||||
| @@ -23,16 +14,10 @@ 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, | ||||
| @@ -55,7 +40,6 @@ declare global { | ||||
|   // for fire event | ||||
|   interface HASSDomEvents { | ||||
|     "reload-lovelace": undefined; | ||||
|     "scroll-to-pos": { x: number; y: number }; | ||||
|   } | ||||
|   // for add event listener | ||||
|   interface HTMLElementEventMap { | ||||
| @@ -91,12 +75,6 @@ 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; | ||||
| @@ -112,13 +90,6 @@ 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 { | ||||
| @@ -131,10 +102,6 @@ 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; | ||||
|   } | ||||
| @@ -190,11 +157,10 @@ 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> | ||||
| @@ -211,75 +177,11 @@ export class HuiDialogEditCard extends LitElement | ||||
|                     dir=${computeRTLDirection(this.hass)} | ||||
|                   > | ||||
|                     <mwc-icon-button> | ||||
|                       <ha-svg-icon .path=${mdiHelpCircleOutline}></ha-svg-icon> | ||||
|                       <ha-svg-icon .path=${mdiHelpCircle}></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"> | ||||
| @@ -288,11 +190,9 @@ 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"> | ||||
| @@ -311,6 +211,22 @@ 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")} | ||||
| @@ -348,13 +264,6 @@ 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; | ||||
| @@ -368,20 +277,10 @@ export class HuiDialogEditCard extends LitElement | ||||
|     this._guiModeAvailable = ev.detail.guiModeAvailable; | ||||
|   } | ||||
|  | ||||
|   private _toggleMode(ev: CustomEvent<RequestSelectedDetail>): void { | ||||
|     if (!shouldHandleRequestSelectedEvent(ev)) { | ||||
|       return; | ||||
|     } | ||||
|   private _toggleMode(): void { | ||||
|     this._cardEditorEl?.toggleMode(); | ||||
|   } | ||||
|  | ||||
|   private _toggleAdvanced(ev: CustomEvent<RequestSelectedDetail>): void { | ||||
|     if (!shouldHandleRequestSelectedEvent(ev)) { | ||||
|       return; | ||||
|     } | ||||
|     this._isAdvanced = !this._isAdvanced; | ||||
|   } | ||||
|  | ||||
|   private _opened() { | ||||
|     this._cardEditorEl?.refreshYamlEditor(); | ||||
|   } | ||||
| @@ -459,45 +358,35 @@ 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%; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         @media all and (min-width: 451px) and (min-height: 501px) { | ||||
|           ha-dialog { | ||||
|             --mdc-dialog-max-width: 90vw; | ||||
|           } | ||||
|           :host([large]) ha-dialog { | ||||
|             width: calc(90vw - 48px); | ||||
|             height: 100%; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         @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; | ||||
|             --mdc-dialog-max-height: calc(100% - 72px); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         ha-dialog { | ||||
|           --mdc-dialog-max-width: 845px; | ||||
|           --dialog-z-index: 5; | ||||
|           --mdc-dialog-max-height: 750px; | ||||
|         } | ||||
|  | ||||
|         :host([large]) ha-dialog { | ||||
|           --mdc-dialog-max-width: calc(100% - 32px); | ||||
|         @media all and (min-width: 451px) and (min-height: 501px) { | ||||
|           ha-dialog { | ||||
|             --mdc-dialog-max-width: 90vw; | ||||
|           } | ||||
|           :host([large]) .content { | ||||
|             width: calc(90vw - 48px); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         ha-header-bar { | ||||
| @@ -518,17 +407,23 @@ 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; | ||||
|           } | ||||
| @@ -540,34 +435,32 @@ export class HuiDialogEditCard extends LitElement | ||||
|           } | ||||
|           .content hui-card-preview { | ||||
|             padding: 8px 10px; | ||||
|             margin: auto; | ||||
|             margin: auto 0px; | ||||
|             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; | ||||
| @@ -575,13 +468,14 @@ 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; | ||||
|   | ||||
| @@ -8,7 +8,7 @@ export interface CreateCardDialogParams { | ||||
|   entities?: string[]; // We can pass entity id's that will be added to the config when a card is picked | ||||
| } | ||||
|  | ||||
| const importCreateCardDialog = () => import("./hui-dialog-create-card"); | ||||
| export const importCreateCardDialog = () => import("./hui-dialog-create-card"); | ||||
|  | ||||
| export const showCreateCardDialog = ( | ||||
|   element: HTMLElement, | ||||
|   | ||||
| @@ -6,7 +6,7 @@ export interface DeleteCardDialogParams { | ||||
|   cardConfig?: LovelaceCardConfig; | ||||
| } | ||||
|  | ||||
| const importDeleteCardDialog = () => import("./hui-dialog-delete-card"); | ||||
| export const importDeleteCardDialog = () => import("./hui-dialog-delete-card"); | ||||
|  | ||||
| export const showDeleteCardDialog = ( | ||||
|   element: HTMLElement, | ||||
|   | ||||
| @@ -8,7 +8,7 @@ export interface EditCardDialogParams { | ||||
|   cardConfig?: LovelaceCardConfig; | ||||
| } | ||||
|  | ||||
| const importEditCardDialog = () => import("./hui-dialog-edit-card"); | ||||
| export const importEditCardDialog = () => import("./hui-dialog-edit-card"); | ||||
|  | ||||
| export const showEditCardDialog = ( | ||||
|   element: HTMLElement, | ||||
|   | ||||
| @@ -14,13 +14,4 @@ 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; | ||||
|   } | ||||
| `; | ||||
|   | ||||
| @@ -19,7 +19,6 @@ 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"; | ||||
|  | ||||
| @@ -38,8 +37,6 @@ 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 { | ||||
| @@ -71,68 +68,62 @@ export class HuiAlarmPanelCardEditor extends LitElement | ||||
|     const states = ["arm_home", "arm_away", "arm_night", "arm_custom_bypass"]; | ||||
|  | ||||
|     return html` | ||||
|       <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> | ||||
|       <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> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -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,7 +22,6 @@ 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"; | ||||
|  | ||||
| @@ -54,8 +53,6 @@ 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 { | ||||
| @@ -110,34 +107,117 @@ export class HuiButtonCardEditor extends LitElement | ||||
|       return html``; | ||||
|     } | ||||
|  | ||||
|     const dir = computeRTLDirection(this.hass!); | ||||
|  | ||||
|     return html` | ||||
|       <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> | ||||
|       <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"> | ||||
|           <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} | ||||
| @@ -147,40 +227,12 @@ 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} | ||||
| @@ -190,44 +242,8 @@ 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> | ||||
|       </hui-config-element-template> | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import { | ||||
|   css, | ||||
|   CSSResultArray, | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
| @@ -24,7 +23,6 @@ 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"; | ||||
|  | ||||
| @@ -43,8 +41,6 @@ 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[]; | ||||
| @@ -73,15 +69,14 @@ export class HuiCalendarCardEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <hui-config-element-template | ||||
|         .hass=${this.hass} | ||||
|         .isAdvanced=${this.isAdvanced} | ||||
|       > | ||||
|         <div class="card-config"> | ||||
|       <div class="card-config"> | ||||
|         <div class="side-by-side"> | ||||
|           <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} | ||||
| @@ -110,30 +105,28 @@ export class HuiCalendarCardEditor extends LitElement | ||||
|             </paper-listbox> | ||||
|           </paper-dropdown-menu> | ||||
|         </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"]} | ||||
|         <hui-theme-select-editor | ||||
|           .hass=${this.hass} | ||||
|           .value=${this._theme} | ||||
|           .configValue=${"theme"} | ||||
|           @value-changed=${this._valueChanged} | ||||
|         > | ||||
|         </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> | ||||
|         ></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> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
| @@ -182,15 +175,8 @@ export class HuiCalendarCardEditor extends LitElement | ||||
|     fireEvent(this, "config-changed", { config: this._config }); | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResultArray { | ||||
|     return [ | ||||
|       configElementStyle, | ||||
|       css` | ||||
|         paper-dropdown-menu { | ||||
|           width: 100%; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   static get styles(): CSSResult { | ||||
|     return configElementStyle; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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,7 +35,6 @@ 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"; | ||||
| @@ -63,8 +62,6 @@ 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[]; | ||||
| @@ -95,7 +92,6 @@ 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} | ||||
|         > | ||||
| @@ -104,80 +100,70 @@ export class HuiEntitiesCardEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <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> | ||||
|       <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)} | ||||
|           > | ||||
|             <ha-switch | ||||
|               .checked=${this._config!.show_header_toggle !== false} | ||||
|               .configValue=${"show_header_toggle"} | ||||
|               @change=${this._valueChanged} | ||||
|             ></ha-switch> | ||||
|           </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-formfield> | ||||
|           <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-settings-row> | ||||
|           <hui-theme-select-editor | ||||
|             .hass=${this.hass} | ||||
|             .value=${this._theme} | ||||
|             .configValue=${"theme"} | ||||
|             @value-changed=${this._valueChanged} | ||||
|           ></hui-theme-select-editor> | ||||
|           </ha-formfield> | ||||
|         </div> | ||||
|       </hui-config-element-template> | ||||
|         <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> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
| @@ -202,9 +188,20 @@ export class HuiEntitiesCardEditor extends LitElement | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (ev.detail && ev.detail.entities) { | ||||
|     if (configValue === "row" || (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) { | ||||
| @@ -224,20 +221,18 @@ export class HuiEntitiesCardEditor extends LitElement | ||||
|  | ||||
|   private _handleSubElementChanged(ev: CustomEvent): void { | ||||
|     ev.stopPropagation(); | ||||
|     const configValue = this._subElementEditorConfig?.type; | ||||
|  | ||||
|     if (!this._config || !this.hass || !configValue) { | ||||
|     if (!this._config || !this.hass) { | ||||
|       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); | ||||
|         goBack = true; | ||||
|         this._goBack(); | ||||
|       } else { | ||||
|         newConfigEntities[this._subElementEditorConfig!.index!] = value; | ||||
|       } | ||||
| @@ -245,10 +240,9 @@ export class HuiEntitiesCardEditor extends LitElement | ||||
|       this._config = { ...this._config!, entities: newConfigEntities }; | ||||
|       this._configEntities = processEditorEntities(this._config!.entities); | ||||
|     } else if (configValue) { | ||||
|       if (!value || value === "") { | ||||
|       if (value === "") { | ||||
|         this._config = { ...this._config }; | ||||
|         delete this._config[configValue!]; | ||||
|         goBack = true; | ||||
|       } else { | ||||
|         this._config = { | ||||
|           ...this._config, | ||||
| @@ -257,21 +251,16 @@ export class HuiEntitiesCardEditor extends LitElement | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (goBack) { | ||||
|       this._goBack(); | ||||
|     } else { | ||||
|       this._subElementEditorConfig = { | ||||
|         ...this._subElementEditorConfig!, | ||||
|         elementConfig: value, | ||||
|       }; | ||||
|     } | ||||
|     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 { | ||||
|   | ||||
| @@ -20,7 +20,6 @@ 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"; | ||||
|  | ||||
| @@ -40,8 +39,6 @@ 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 { | ||||
| @@ -79,67 +76,74 @@ export class HuiEntityCardEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <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> | ||||
|       <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"> | ||||
|           <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-config-element-template> | ||||
|         <hui-theme-select-editor | ||||
|           .hass=${this.hass} | ||||
|           .value=${this._theme} | ||||
|           .configValue=${"theme"} | ||||
|           @value-changed=${this._valueChanged} | ||||
|         ></hui-theme-select-editor> | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import "@polymer/paper-input/paper-input"; | ||||
| import { | ||||
|   CSSResult, | ||||
|   css, | ||||
|   CSSResultArray, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
| @@ -10,15 +11,14 @@ 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,8 +40,6 @@ 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 { | ||||
| @@ -83,131 +81,141 @@ export class HuiGaugeCardEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <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( | ||||
|       <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( | ||||
|               "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 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 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._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._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> | ||||
|                 .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> | ||||
|           ` | ||||
|           : ""} | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResult { | ||||
|     return configElementStyle; | ||||
|   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; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   } | ||||
|  | ||||
|   private _toggleSeverity(ev: EntitiesEditorEvent): void { | ||||
|   | ||||
| @@ -76,7 +76,6 @@ 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} | ||||
|   | ||||
| @@ -21,18 +21,17 @@ 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, | ||||
| @@ -58,8 +57,6 @@ 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[]; | ||||
| @@ -103,94 +100,103 @@ export class HuiGlanceCardEditor extends LitElement | ||||
|       return html``; | ||||
|     } | ||||
|  | ||||
|     const dir = computeRTLDirection(this.hass!); | ||||
|  | ||||
|     return html` | ||||
|       <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> | ||||
|       <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-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> | ||||
|       </hui-config-element-template> | ||||
|         <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> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -24,7 +24,6 @@ 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"; | ||||
| @@ -50,8 +49,6 @@ 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[]; | ||||
| @@ -84,48 +81,47 @@ export class HuiHistoryGraphCardEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <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 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"> | ||||
|       <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"> | ||||
|           <paper-input | ||||
|             type="number" | ||||
|             .label=${this.hass.localize( | ||||
|             .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> | ||||
|           <paper-input | ||||
|             type="number" | ||||
|             .label="${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.generic.refresh_interval" | ||||
|             )} | ||||
|             .value=${this._refresh_interval} | ||||
|             )} (${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.config.optional" | ||||
|             )})" | ||||
|             .value="${this._refresh_interval}" | ||||
|             .configValue=${"refresh_interval"} | ||||
|             @value-changed=${this._valueChanged} | ||||
|             @value-changed="${this._valueChanged}" | ||||
|           ></paper-input> | ||||
|         </div> | ||||
|       </hui-config-element-template> | ||||
|         <hui-entity-editor | ||||
|           .hass=${this.hass} | ||||
|           .entities="${this._configEntities}" | ||||
|           @entities-changed="${this._valueChanged}" | ||||
|         ></hui-entity-editor> | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,6 @@ 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"; | ||||
|  | ||||
| @@ -33,8 +32,6 @@ 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 { | ||||
| @@ -60,42 +57,37 @@ export class HuiHumidifierCardEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <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> | ||||
|       <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> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,6 @@ 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"; | ||||
|  | ||||
| @@ -29,8 +28,6 @@ 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 { | ||||
| @@ -56,41 +53,40 @@ export class HuiIframeCardEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <hui-config-element-template | ||||
|         .hass=${this.hass} | ||||
|         .isAdvanced=${this.isAdvanced} | ||||
|       > | ||||
|         <div class="card-config"> | ||||
|       <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"> | ||||
|           <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> | ||||
|           <paper-input | ||||
|             .label=${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.generic.title" | ||||
|             )} | ||||
|             .value=${this._title} | ||||
|             .configValue=${"title"} | ||||
|             @value-changed=${this._valueChanged} | ||||
|             )} (${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.config.optional" | ||||
|             )})" | ||||
|             .value="${this._title}" | ||||
|             .configValue="${"title"}" | ||||
|             @value-changed="${this._valueChanged}" | ||||
|           ></paper-input> | ||||
|         </div> | ||||
|         <div slot="advanced" class="card-config"> | ||||
|           <paper-input | ||||
|             .label=${this.hass.localize( | ||||
|             .label="${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.generic.aspect_ratio" | ||||
|             )} | ||||
|             .value=${this._aspect_ratio} | ||||
|             .configValue=${"aspect_ratio"} | ||||
|             @value-changed=${this._valueChanged} | ||||
|             )} (${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.config.optional" | ||||
|             )})" | ||||
|             .value="${this._aspect_ratio}" | ||||
|             .configValue="${"aspect_ratio"}" | ||||
|             @value-changed="${this._valueChanged}" | ||||
|           ></paper-input> | ||||
|         </div> | ||||
|       </hui-config-element-template> | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,6 @@ 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"; | ||||
|  | ||||
| @@ -40,8 +39,6 @@ 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 { | ||||
| @@ -88,73 +85,78 @@ export class HuiLightCardEditor extends LitElement | ||||
|     ]; | ||||
|  | ||||
|     return html` | ||||
|       <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} | ||||
|             @value-changed=${this._valueChanged} | ||||
|             allow-custom-entity | ||||
|           ></ha-entity-picker> | ||||
|       <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 | ||||
|             .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-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-config-element-template> | ||||
|  | ||||
|         <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> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -17,7 +17,6 @@ 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"; | ||||
|  | ||||
| @@ -34,8 +33,6 @@ 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[]; | ||||
| @@ -68,51 +65,50 @@ export class HuiLogbookCardEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <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"> | ||||
|       <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-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> | ||||
|       </hui-config-element-template> | ||||
|         <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> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -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,7 +29,6 @@ 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, | ||||
| @@ -53,8 +52,6 @@ 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[]; | ||||
| @@ -97,84 +94,87 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor { | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <hui-config-element-template | ||||
|         .hass=${this.hass} | ||||
|         .isAdvanced=${this.isAdvanced} | ||||
|       > | ||||
|         <div class="card-config"> | ||||
|       <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"> | ||||
|           <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 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} | ||||
|             .entities=${this._configEntities} | ||||
|             @entities-changed=${this._entitiesValueChanged} | ||||
|           ></hui-entity-editor> | ||||
|         </div> | ||||
|         <div slot="advanced" class="card-config"> | ||||
|           <paper-input | ||||
|             .label=${this.hass.localize( | ||||
|             .label="${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.generic.aspect_ratio" | ||||
|             )} | ||||
|             .value=${this._aspect_ratio} | ||||
|             .configValue=${"aspect_ratio"} | ||||
|             @value-changed=${this._valueChanged} | ||||
|             )} (${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> | ||||
|           <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> | ||||
|         <div class="side-by-side"> | ||||
|           <ha-formfield | ||||
|             .label=${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.map.dark_mode" | ||||
|             )} | ||||
|             .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}" | ||||
|           ></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" | ||||
|             )} | ||||
|             .hass=${this.hass} | ||||
|             .value="${this._geo_location_sources}" | ||||
|             .configValue="${"geo_location_sources"}" | ||||
|             @value-changed="${this._valueChanged}" | ||||
|           ></hui-input-list-editor> | ||||
|         </div> | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,6 @@ 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"; | ||||
|  | ||||
| @@ -31,8 +30,6 @@ 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 { | ||||
| @@ -58,43 +55,38 @@ export class HuiMarkdownCardEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <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> | ||||
|       <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> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -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} | ||||
|           @change="${this._valueChanged}" | ||||
|           allow-custom-entity | ||||
|         ></ha-entity-picker> | ||||
|       </div> | ||||
|     `; | ||||
|   | ||||
| @@ -16,7 +16,6 @@ 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"; | ||||
|  | ||||
| @@ -33,8 +32,6 @@ 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 { | ||||
| @@ -66,51 +63,50 @@ export class HuiPictureCardEditor extends LitElement | ||||
|     const actions = ["navigate", "url", "call-service", "none"]; | ||||
|  | ||||
|     return html` | ||||
|       <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.image" | ||||
|             )} (${this.hass.localize( | ||||
|               "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"> | ||||
|       <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 | ||||
|             .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> | ||||
|           <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> | ||||
|       </hui-config-element-template> | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -3,8 +3,7 @@ import "@polymer/paper-input/paper-input"; | ||||
| import "@polymer/paper-item/paper-item"; | ||||
| import "@polymer/paper-listbox/paper-listbox"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResultArray, | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
| @@ -14,8 +13,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"; | ||||
| @@ -24,7 +23,6 @@ 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"; | ||||
|  | ||||
| @@ -50,8 +48,6 @@ 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 { | ||||
| @@ -110,137 +106,148 @@ 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` | ||||
|       <hui-config-element-template | ||||
|         .hass=${this.hass} | ||||
|         .isAdvanced=${this.isAdvanced} | ||||
|       > | ||||
|         <div class="card-config"> | ||||
|           <ha-entity-picker | ||||
|             allow-custom-entity | ||||
|       <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 | ||||
|             .label="${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.generic.entity" | ||||
|               "ui.panel.lovelace.editor.card.generic.camera_view" | ||||
|             )} (${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.config.required" | ||||
|               "ui.panel.lovelace.editor.card.config.optional" | ||||
|             )})" | ||||
|             .hass=${this.hass} | ||||
|             .value=${this._entity} | ||||
|             .configValue=${"entity"} | ||||
|             @value-changed=${this._valueChanged} | ||||
|           ></ha-entity-picker> | ||||
|             .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> | ||||
|           <paper-input | ||||
|             .label=${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.generic.name" | ||||
|             )} | ||||
|             .value=${this._name} | ||||
|             .configValue=${"name"} | ||||
|             @value-changed=${this._valueChanged} | ||||
|             .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.generic.image" | ||||
|             )} | ||||
|             .value=${this._image} | ||||
|             .configValue=${"image"} | ||||
|             @value-changed=${this._valueChanged} | ||||
|           ></paper-input> | ||||
|           <ha-settings-row three-line> | ||||
|             <span slot="heading"> | ||||
|               ${this.hass.localize( | ||||
|         </div> | ||||
|         <div class="side-by-side"> | ||||
|           <div> | ||||
|             <ha-formfield | ||||
|               .label=${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._change} | ||||
|             ></ha-switch> | ||||
|           </ha-settings-row> | ||||
|           <ha-settings-row three-line> | ||||
|             <span slot="heading"> | ||||
|               ${this.hass.localize( | ||||
|               .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( | ||||
|                 "ui.panel.lovelace.editor.card.generic.show_state" | ||||
|               )} | ||||
|             </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( | ||||
|               "ui.panel.lovelace.editor.card.generic.tap_action" | ||||
|             )} | ||||
|             .hass=${this.hass} | ||||
|             .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} | ||||
|               .dir=${dir} | ||||
|             > | ||||
|               <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> | ||||
|               <ha-switch | ||||
|                 .checked="${this._config!.show_state !== false}" | ||||
|                 .configValue="${"show_state"}" | ||||
|                 @change="${this._change}" | ||||
|               ></ha-switch | ||||
|             ></ha-formfield> | ||||
|           </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 class="side-by-side"> | ||||
|           <hui-action-editor | ||||
|             .label=${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.generic.hold_action" | ||||
|             )} | ||||
|             .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._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> | ||||
|           <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-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> | ||||
|       </hui-config-element-template> | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
| @@ -287,15 +294,8 @@ export class HuiPictureEntityCardEditor extends LitElement | ||||
|     fireEvent(this, "config-changed", { config: this._config }); | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResultArray { | ||||
|     return [ | ||||
|       configElementStyle, | ||||
|       css` | ||||
|         paper-dropdown-menu { | ||||
|           width: 100%; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   static get styles(): CSSResult { | ||||
|     return configElementStyle; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -3,8 +3,7 @@ import "@polymer/paper-input/paper-input"; | ||||
| import "@polymer/paper-item/paper-item"; | ||||
| import "@polymer/paper-listbox/paper-listbox"; | ||||
| import { | ||||
|   css, | ||||
|   CSSResultArray, | ||||
|   CSSResult, | ||||
|   customElement, | ||||
|   html, | ||||
|   internalProperty, | ||||
| @@ -23,7 +22,6 @@ 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, | ||||
| @@ -53,8 +51,6 @@ 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[]; | ||||
| @@ -116,114 +112,120 @@ export class HuiPictureGlanceCardEditor extends LitElement | ||||
|     const views = ["auto", "live"]; | ||||
|  | ||||
|     return html` | ||||
|       <hui-config-element-template | ||||
|         .hass=${this.hass} | ||||
|         .isAdvanced=${this.isAdvanced} | ||||
|       > | ||||
|         <div class="card-config"> | ||||
|       <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> | ||||
|           <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 | ||||
|             .label=${this.hass.localize( | ||||
|               "ui.panel.lovelace.editor.card.generic.image" | ||||
|             )} | ||||
|             .value=${this._image} | ||||
|             .configValue=${"image"} | ||||
|             @value-changed=${this._valueChanged} | ||||
|             .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> | ||||
|         </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} | ||||
|             .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}" | ||||
|           ></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> | ||||
|         <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> | ||||
|         <hui-theme-select-editor | ||||
|           .hass=${this.hass} | ||||
|           .value="${this._theme}" | ||||
|           .configValue="${"theme"}" | ||||
|           @value-changed="${this._valueChanged}" | ||||
|         ></hui-theme-select-editor> | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
| @@ -256,15 +258,8 @@ export class HuiPictureGlanceCardEditor extends LitElement | ||||
|     fireEvent(this, "config-changed", { config: this._config }); | ||||
|   } | ||||
|  | ||||
|   static get styles(): CSSResultArray { | ||||
|     return [ | ||||
|       configElementStyle, | ||||
|       css` | ||||
|         paper-dropdown-menu { | ||||
|           width: 100%; | ||||
|         } | ||||
|       `, | ||||
|     ]; | ||||
|   static get styles(): CSSResult { | ||||
|     return configElementStyle; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,6 @@ 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"; | ||||
|  | ||||
| @@ -34,8 +33,6 @@ 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 { | ||||
| @@ -61,42 +58,37 @@ export class HuiPlantStatusCardEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <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> | ||||
|       <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> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -17,13 +17,11 @@ 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"; | ||||
|  | ||||
| @@ -46,8 +44,6 @@ 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 { | ||||
| @@ -95,99 +91,105 @@ export class HuiSensorCardEditor extends LitElement | ||||
|     const graphs = ["line", "none"]; | ||||
|  | ||||
|     return html` | ||||
|       <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 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> | ||||
|       <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 | ||||
|             .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> | ||||
|           <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> | ||||
|       </hui-config-element-template> | ||||
|       </div> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -4,20 +4,19 @@ 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(), | ||||
| @@ -30,8 +29,6 @@ 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 { | ||||
| @@ -53,38 +50,33 @@ export class HuiShoppingListEditor extends LitElement | ||||
|     } | ||||
|  | ||||
|     return html` | ||||
|       <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> | ||||
|       <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> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user