diff --git a/gallery/src/demos/demo-hui-glance-card.ts b/gallery/src/demos/demo-hui-glance-card.ts index 844b7c4656..6df5d2462a 100644 --- a/gallery/src/demos/demo-hui-glance-card.ts +++ b/gallery/src/demos/demo-hui-glance-card.ts @@ -172,10 +172,14 @@ const CONFIGS = [ - type: glance entities: - entity: lock.kitchen_door - tap_action: toggle + tap_action: + type: toggle - entity: light.ceiling_lights - tap_action: call-service - service: light.turn_on + tap_action: + action: call-service + service: light.turn_on + service_data: + entity_id: light.ceiling_lights - device_tracker.demo_paulus - media_player.living_room - sun.sun diff --git a/gallery/src/demos/demo-hui-picture-elements-card.ts b/gallery/src/demos/demo-hui-picture-elements-card.ts index 53cb27f585..aa991d7706 100644 --- a/gallery/src/demos/demo-hui-picture-elements-card.ts +++ b/gallery/src/demos/demo-hui-picture-elements-card.ts @@ -56,7 +56,8 @@ const CONFIGS = [ --iron-icon-fill-color: rgba(50, 50, 50, .75) - type: image entity: light.bed_light - tap_action: toggle + tap_action: + action: toggle image: /images/light_bulb_off.png state_image: 'on': /images/light_bulb_on.png diff --git a/gallery/src/demos/demo-hui-picture-glance-card.ts b/gallery/src/demos/demo-hui-picture-glance-card.ts index 18b5643267..2ab8d7c32c 100644 --- a/gallery/src/demos/demo-hui-picture-glance-card.ts +++ b/gallery/src/demos/demo-hui-picture-glance-card.ts @@ -83,6 +83,23 @@ const CONFIGS = [ - binary_sensor.basement_floor_wet `, }, + { + heading: "Custom tap action", + config: ` +- type: picture-glance + image: /images/living_room.png + title: Living room + entity: light.ceiling_lights + tap_action: + action: toggle + entities: + - entity: switch.decorative_lights + icon: mdi:power + tap_action: + action: toggle + - binary_sensor.basement_floor_wet + `, + }, ]; class DemoPicGlance extends PolymerElement { diff --git a/script/develop b/script/develop index 59d778a2f2..24c1253dc0 100755 --- a/script/develop +++ b/script/develop @@ -4,6 +4,8 @@ # Stop on errors set -e +cd "$(dirname "$0")/.." + BUILD_DIR=build OUTPUT_DIR=hass_frontend OUTPUT_DIR_ES5=hass_frontend_es5 diff --git a/script/size_stats b/script/size_stats new file mode 100755 index 0000000000..4b6601a480 --- /dev/null +++ b/script/size_stats @@ -0,0 +1,11 @@ +#!/bin/sh +# Analyze stats + +# Stop on errors +set -e + +cd "$(dirname "$0")/.." + +STATS=1 NODE_ENV=production webpack --profile --json > compilation-stats.json +npx webpack-bundle-analyzer compilation-stats.json hass_frontend +rm compilation-stats.json diff --git a/src/common/dom/setup-leaflet-map.js b/src/common/dom/setup-leaflet-map.js index de1a0109c0..7649161de0 100644 --- a/src/common/dom/setup-leaflet-map.js +++ b/src/common/dom/setup-leaflet-map.js @@ -1,7 +1,9 @@ -import Leaflet from "leaflet"; - // Sets up a Leaflet map on the provided DOM element -export default function setupLeafletMap(mapElement) { +export const setupLeafletMap = async (mapElement) => { + const Leaflet = (await import(/* webpackChunkName: "leaflet" */ "leaflet")) + .default; + Leaflet.Icon.Default.imagePath = "/static/images/leaflet"; + const map = Leaflet.map(mapElement); const style = document.createElement("link"); style.setAttribute("href", "/static/images/leaflet/leaflet.css"); @@ -21,5 +23,5 @@ export default function setupLeafletMap(mapElement) { } ).addTo(map); - return map; -} + return [map, Leaflet]; +}; diff --git a/src/data/lovelace.ts b/src/data/lovelace.ts index cd806b682b..054a79ce26 100644 --- a/src/data/lovelace.ts +++ b/src/data/lovelace.ts @@ -51,9 +51,13 @@ export type ActionConfig = | MoreInfoActionConfig | NoActionConfig; -export const fetchConfig = (hass: HomeAssistant): Promise => +export const fetchConfig = ( + hass: HomeAssistant, + force: boolean +): Promise => hass.callWS({ type: "lovelace/config", + force, }); export const migrateConfig = (hass: HomeAssistant): Promise => diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index 11204aa5fc..b161d8bb59 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -8,18 +8,18 @@ import { TemplateResult } from "lit-html"; import { fireEvent } from "../../../common/dom/fire_event"; import { styleMap } from "lit-html/directives/styleMap"; -import { jQuery } from "../../../resources/jquery"; -import { roundSliderStyle } from "../../../resources/jquery.roundslider"; import { HomeAssistant, LightEntity } from "../../../types"; import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin"; import { LovelaceCard } from "../types"; import { LovelaceCardConfig } from "../../../data/lovelace"; import { longPress } from "../common/directives/long-press-directive"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { loadRoundslider } from "../../../resources/jquery.roundslider.ondemand"; +import { toggleEntity } from "../common/entity/toggle-entity"; import stateIcon from "../../../common/entity/state_icon"; import computeStateName from "../../../common/entity/compute_state_name"; import applyThemesOnElement from "../../../common/dom/apply_themes_on_element"; -import { hasConfigOrEntityChanged } from "../common/has-changed"; import "../../../components/ha-card"; import "../../../components/ha-icon"; @@ -49,11 +49,15 @@ export class HuiLightCard extends hassLocalizeLitMixin(LitElement) public hass?: HomeAssistant; private _config?: Config; private _brightnessTimout?: number; + private _roundSliderStyle?: TemplateResult; + private _jQuery?: any; static get properties(): PropertyDeclarations { return { hass: {}, _config: {}, + roundSliderStyle: {}, + _jQuery: {}, }; } @@ -99,14 +103,14 @@ export class HuiLightCard extends hassLocalizeLitMixin(LitElement) color: this._computeColor(stateObj), }) }" - @ha-click="${() => this._handleClick(false)}" - @ha-hold="${() => this._handleClick(true)}" + @ha-click="${this._handleTap}" + @ha-hold="${this._handleHold}" .longPress="${longPress()}" >
@@ -124,10 +128,15 @@ export class HuiLightCard extends hassLocalizeLitMixin(LitElement) return hasConfigOrEntityChanged(this, changedProps); } - protected firstUpdated(): void { + protected async firstUpdated(): Promise { + const loaded = await loadRoundslider(); + + this._roundSliderStyle = loaded.roundSliderStyle; + this._jQuery = loaded.jQuery; + const brightness = this.hass!.states[this._config!.entity].attributes .brightness; - jQuery("#light", this.shadowRoot).roundSlider({ + this._jQuery("#light", this.shadowRoot).roundSlider({ ...lightConfig, change: (value) => this._setBrightness(value), drag: (value) => this._dragEvent(value), @@ -139,13 +148,13 @@ export class HuiLightCard extends hassLocalizeLitMixin(LitElement) } protected updated(changedProps: PropertyValues): void { - if (!this._config || !this.hass) { + if (!this._config || !this.hass || !this._jQuery) { return; } const attrs = this.hass!.states[this._config!.entity].attributes; - jQuery("#light", this.shadowRoot).roundSlider({ + this._jQuery("#light", this.shadowRoot).roundSlider({ value: Math.round((attrs.brightness / 254) * 100) || 0, }); @@ -157,7 +166,7 @@ export class HuiLightCard extends hassLocalizeLitMixin(LitElement) private renderStyle(): TemplateResult { return html` - ${roundSliderStyle} + ${this._roundSliderStyle}
+ ${ + this.localize("ui.panel.lovelace.editor.edit_card.edit") + } ${ this.localize("ui.panel.lovelace.editor.edit_card.delete") }${ - this.localize("ui.panel.lovelace.editor.edit_card.edit") - }
`; diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index d612d60bb7..93c159becd 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -1,5 +1,4 @@ import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; -import "@polymer/paper-button/paper-button"; import { TemplateResult } from "lit-html"; import { HomeAssistant } from "../../../types"; @@ -84,9 +83,6 @@ export class HuiEntityEditor extends LitElement { .entities { padding-left: 20px; } - paper-button { - margin: 8px 0; - } `; } diff --git a/src/panels/lovelace/editor/config-elements/hui-view-editor.ts b/src/panels/lovelace/editor/config-elements/hui-view-editor.ts index 872c776663..64ee490639 100644 --- a/src/panels/lovelace/editor/config-elements/hui-view-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-view-editor.ts @@ -28,7 +28,8 @@ export class HuiViewEditor extends hassLocalizeLitMixin(LitElement) { if (!this._config) { return ""; } - return this._config.id || ""; + + return "id" in this._config ? this._config.id! : ""; } get _title(): string { diff --git a/src/panels/lovelace/editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/hui-dialog-edit-card.ts index 0516753e64..0b5c3f7112 100644 --- a/src/panels/lovelace/editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/hui-dialog-edit-card.ts @@ -2,7 +2,7 @@ import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; import { TemplateResult } from "lit-html"; import { HomeAssistant } from "../../../types"; -import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event"; +import { HASSDomEvent } from "../../../common/dom/fire_event"; import { LovelaceCardConfig } from "../../../data/lovelace"; import "./hui-edit-card"; import "./hui-migrate-config"; @@ -11,7 +11,6 @@ declare global { // for fire event interface HASSDomEvents { "reload-lovelace": undefined; - "show-edit-card": EditCardDialogParams; } // for add event listener interface HTMLElementEventMap { @@ -19,10 +18,6 @@ declare global { } } -let registeredDialog = false; -const dialogShowEvent = "show-edit-card"; -const dialogTag = "hui-dialog-edit-card"; - export interface EditCardDialogParams { cardConfig?: LovelaceCardConfig; viewId?: string | number; @@ -30,24 +25,6 @@ export interface EditCardDialogParams { reloadLovelace: () => void; } -const registerEditCardDialog = (element: HTMLElement) => - fireEvent(element, "register-dialog", { - dialogShowEvent, - dialogTag, - dialogImport: () => import("./hui-dialog-edit-card"), - }); - -export const showEditCardDialog = ( - element: HTMLElement, - editCardDialogParams: EditCardDialogParams -) => { - if (!registeredDialog) { - registeredDialog = true; - registerEditCardDialog(element); - } - fireEvent(element, dialogShowEvent, editCardDialogParams); -}; - export class HuiDialogEditCard extends LitElement { protected hass?: HomeAssistant; private _params?: EditCardDialogParams; @@ -110,4 +87,4 @@ declare global { } } -customElements.define(dialogTag, HuiDialogEditCard); +customElements.define("hui-dialog-edit-card", HuiDialogEditCard); diff --git a/src/panels/lovelace/editor/hui-dialog-edit-view.ts b/src/panels/lovelace/editor/hui-dialog-edit-view.ts index f962e4d43e..3f4d13129f 100644 --- a/src/panels/lovelace/editor/hui-dialog-edit-view.ts +++ b/src/panels/lovelace/editor/hui-dialog-edit-view.ts @@ -2,16 +2,15 @@ import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; import { TemplateResult } from "lit-html"; import { HomeAssistant } from "../../../types"; -import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event"; -import { LovelaceViewConfig } from "../../../data/lovelace"; +import { HASSDomEvent } from "../../../common/dom/fire_event"; import "./hui-edit-view"; import "./hui-migrate-config"; +import { EditViewDialogParams } from "./show-edit-view-dialog"; declare global { // for fire event interface HASSDomEvents { "reload-lovelace": undefined; - "show-edit-view": EditViewDialogParams; } // for add event listener interface HTMLElementEventMap { @@ -19,34 +18,6 @@ declare global { } } -let registeredDialog = false; -const dialogShowEvent = "show-edit-view"; -const dialogTag = "hui-dialog-edit-view"; - -export interface EditViewDialogParams { - viewConfig?: LovelaceViewConfig; - add?: boolean; - reloadLovelace: () => void; -} - -const registerEditViewDialog = (element: HTMLElement) => - fireEvent(element, "register-dialog", { - dialogShowEvent, - dialogTag, - dialogImport: () => import("./hui-dialog-edit-view"), - }); - -export const showEditViewDialog = ( - element: HTMLElement, - editViewDialogParams: EditViewDialogParams -) => { - if (!registeredDialog) { - registeredDialog = true; - registerEditViewDialog(element); - } - fireEvent(element, dialogShowEvent, editViewDialogParams); -}; - export class HuiDialogEditView extends LitElement { protected hass?: HomeAssistant; private _params?: EditViewDialogParams; @@ -71,7 +42,7 @@ export class HuiDialogEditView extends LitElement { if ( !this._params.add && this._params.viewConfig && - !this._params.viewConfig.id + !("id" in this._params.viewConfig) ) { return html` ${this.localize("ui.common.save")} + + + + Delete + +
`; @@ -189,6 +204,20 @@ export class HuiEditView extends hassLocalizeLitMixin(LitElement) { this._updateConfigInBackend(); } + private _delete() { + if (this._config!.cards && this._config!.cards!.length > 0) { + alert( + "You can't delete a view that has card in them. Remove the cards first." + ); + return; + } + confDeleteView(this.hass!, this._config!.id!, () => { + this._closeDialog(); + this.reloadLovelace!(); + navigate(this, `/lovelace/0`); + }); + } + private async _resizeDialog(): Promise { await this.updateComplete; fireEvent(this._dialog, "iron-resize"); @@ -233,7 +262,7 @@ export class HuiEditView extends hassLocalizeLitMixin(LitElement) { } else { await updateViewConfig( this.hass!, - this.viewConfig!.id!, + String(this.viewConfig!.id!), this._config, "json" ); diff --git a/src/panels/lovelace/editor/show-edit-card-dialog.ts b/src/panels/lovelace/editor/show-edit-card-dialog.ts new file mode 100644 index 0000000000..dfd8ee379a --- /dev/null +++ b/src/panels/lovelace/editor/show-edit-card-dialog.ts @@ -0,0 +1,38 @@ +import { LovelaceCardConfig } from "../../../data/lovelace"; +import { fireEvent } from "../../../common/dom/fire_event"; + +declare global { + // for fire event + interface HASSDomEvents { + "show-edit-card": EditCardDialogParams; + } +} + +let registeredDialog = false; +const dialogShowEvent = "show-edit-card"; +const dialogTag = "hui-dialog-edit-card"; + +export interface EditCardDialogParams { + cardConfig?: LovelaceCardConfig; + viewId?: string | number; + add: boolean; + reloadLovelace: () => void; +} + +const registerEditCardDialog = (element: HTMLElement) => + fireEvent(element, "register-dialog", { + dialogShowEvent, + dialogTag, + dialogImport: () => import("./hui-dialog-edit-card"), + }); + +export const showEditCardDialog = ( + element: HTMLElement, + editCardDialogParams: EditCardDialogParams +) => { + if (!registeredDialog) { + registeredDialog = true; + registerEditCardDialog(element); + } + fireEvent(element, dialogShowEvent, editCardDialogParams); +}; diff --git a/src/panels/lovelace/editor/show-edit-view-dialog.ts b/src/panels/lovelace/editor/show-edit-view-dialog.ts new file mode 100644 index 0000000000..b2f76be9e6 --- /dev/null +++ b/src/panels/lovelace/editor/show-edit-view-dialog.ts @@ -0,0 +1,42 @@ +import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event"; +import { LovelaceViewConfig } from "../../../data/lovelace"; + +declare global { + // for fire event + interface HASSDomEvents { + "reload-lovelace": undefined; + "show-edit-view": EditViewDialogParams; + } + // for add event listener + interface HTMLElementEventMap { + "reload-lovelace": HASSDomEvent; + } +} + +let registeredDialog = false; +const dialogShowEvent = "show-edit-view"; +const dialogTag = "hui-dialog-edit-view"; + +export interface EditViewDialogParams { + viewConfig?: LovelaceViewConfig; + add?: boolean; + reloadLovelace: () => void; +} + +const registerEditViewDialog = (element: HTMLElement) => + fireEvent(element, "register-dialog", { + dialogShowEvent, + dialogTag, + dialogImport: () => import("./hui-dialog-edit-view"), + }); + +export const showEditViewDialog = ( + element: HTMLElement, + editViewDialogParams: EditViewDialogParams +) => { + if (!registeredDialog) { + registeredDialog = true; + registerEditViewDialog(element); + } + fireEvent(element, dialogShowEvent, editViewDialogParams); +}; diff --git a/src/panels/lovelace/elements/types.ts b/src/panels/lovelace/elements/types.ts index 0eb49c5308..ebc66605d9 100644 --- a/src/panels/lovelace/elements/types.ts +++ b/src/panels/lovelace/elements/types.ts @@ -8,7 +8,6 @@ export interface LovelaceElementConfig { hold_action?: ActionConfig; service?: string; service_data?: object; - navigation_path?: string; tap_action?: ActionConfig; title?: string; } diff --git a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts index 79b1707774..de2f7f2fbb 100644 --- a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts @@ -8,11 +8,15 @@ import "./hui-error-entity-row"; import { HomeAssistant } from "../../../types"; import { EntityRow, EntityConfig } from "./types"; +import computeStateDisplay from "../../../common/entity/compute_state_display"; +import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin"; + interface SensorEntityConfig extends EntityConfig { format?: "relative" | "date" | "time" | "datetime"; } -class HuiSensorEntityRow extends LitElement implements EntityRow { +class HuiSensorEntityRow extends hassLocalizeLitMixin(LitElement) + implements EntityRow { public hass?: HomeAssistant; private _config?: SensorEntityConfig; @@ -58,7 +62,7 @@ class HuiSensorEntityRow extends LitElement implements EntityRow { .format="${this._config.format}" > ` - : stateObj.state + : computeStateDisplay(this.localize, stateObj, this.hass.language) } diff --git a/src/panels/lovelace/ha-panel-lovelace.js b/src/panels/lovelace/ha-panel-lovelace.js index c7b8fa27a8..936b0e5d1a 100644 --- a/src/panels/lovelace/ha-panel-lovelace.js +++ b/src/panels/lovelace/ha-panel-lovelace.js @@ -28,7 +28,7 @@ class Lovelace extends localizeMixin(PolymerElement) { route="[[route]]" config="[[_config]]" columns="[[_columns]]" - on-config-refresh="_fetchConfig" + on-config-refresh="_forceFetchConfig" > -
+