Speed things up (#2270)

* Speed things up

* Fixes
This commit is contained in:
Paulus Schoutsen 2018-12-12 08:51:59 +01:00 committed by GitHub
parent 7cd6619a79
commit edef4ba2f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 137 additions and 82 deletions

View File

@ -0,0 +1,3 @@
export const afterNextRender = (cb: () => void): void => {
requestAnimationFrame(() => setTimeout(cb, 0));
};

View File

@ -84,7 +84,9 @@ export class HaStateLabelBadge extends hassLocalizeLitMixin(LitElement) {
super.firstUpdated(changedProperties); super.firstUpdated(changedProperties);
this.addEventListener("click", (ev) => { this.addEventListener("click", (ev) => {
ev.stopPropagation(); ev.stopPropagation();
fireEvent(this, "hass-more-info", { entityId: this.state!.entity_id }); if (this.state) {
fireEvent(this, "hass-more-info", { entityId: this.state.entity_id });
}
}); });
} }

View File

@ -37,6 +37,7 @@ export const hassLocalizeLitMixin = <T extends LitElement>(
public connectedCallback(): void { public connectedCallback(): void {
super.connectedCallback(); super.connectedCallback();
if (this.localize === empty) {
let language; let language;
let resources; let resources;
if (this.hass) { if (this.hass) {
@ -45,6 +46,7 @@ export const hassLocalizeLitMixin = <T extends LitElement>(
} }
this.localize = this.__computeLocalize(language, resources); this.localize = this.__computeLocalize(language, resources);
} }
}
public updated(changedProperties: PropertyValues) { public updated(changedProperties: PropertyValues) {
super.updated(changedProperties); super.updated(changedProperties);

View File

@ -19,6 +19,7 @@ import { LovelaceCardConfig } from "../../../data/lovelace";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-icon"; import "../../../components/ha-icon";
import { loadRoundslider } from "../../../resources/jquery.roundslider.ondemand"; import { loadRoundslider } from "../../../resources/jquery.roundslider.ondemand";
import { afterNextRender } from "../../../common/util/render-status";
const thermostatConfig = { const thermostatConfig = {
radius: 150, radius: 150,
@ -68,6 +69,7 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
private _config?: Config; private _config?: Config;
private _roundSliderStyle?: TemplateResult; private _roundSliderStyle?: TemplateResult;
private _jQuery?: any; private _jQuery?: any;
private _broadCard?: boolean;
static get properties(): PropertyDeclarations { static get properties(): PropertyDeclarations {
return { return {
@ -95,7 +97,6 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
return html``; return html``;
} }
const stateObj = this.hass.states[this._config.entity] as ClimateEntity; const stateObj = this.hass.states[this._config.entity] as ClimateEntity;
const broadCard = this.clientWidth > 390;
const mode = modeIcons[stateObj.attributes.operation_mode || ""] const mode = modeIcons[stateObj.attributes.operation_mode || ""]
? stateObj.attributes.operation_mode! ? stateObj.attributes.operation_mode!
: "unknown-mode"; : "unknown-mode";
@ -104,8 +105,8 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
<ha-card <ha-card
class="${classMap({ class="${classMap({
[mode]: true, [mode]: true,
large: broadCard, large: this._broadCard!,
small: !broadCard, small: !this._broadCard,
})}"> })}">
<div id="root"> <div id="root">
<div id="thermostat"></div> <div id="thermostat"></div>
@ -146,8 +147,41 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
return hasConfigOrEntityChanged(this, changedProps); return hasConfigOrEntityChanged(this, changedProps);
} }
protected async firstUpdated(): Promise<void> { protected firstUpdated(): void {
this._initialLoad();
}
protected updated(changedProps: PropertyValues): void {
if (!this._config || !this.hass || !changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.themes !== this.hass.themes) {
applyThemesOnElement(this, this.hass.themes, this._config.theme);
}
const stateObj = this.hass.states[this._config.entity] as ClimateEntity;
if (
this._jQuery &&
// If jQuery changed, we just rendered in firstUpdated
!changedProps.has("_jQuery") &&
(!oldHass || oldHass.states[this._config.entity] !== stateObj)
) {
const [sliderValue, uiValue] = this._genSliderValue(stateObj);
this._jQuery("#thermostat", this.shadowRoot).roundSlider({
value: sliderValue,
});
this._updateSetTemp(uiValue);
}
}
private async _initialLoad(): Promise<void> {
const loaded = await loadRoundslider(); const loaded = await loadRoundslider();
await new Promise((resolve) => afterNextRender(resolve));
this._roundSliderStyle = loaded.roundSliderStyle; this._roundSliderStyle = loaded.roundSliderStyle;
this._jQuery = loaded.jQuery; this._jQuery = loaded.jQuery;
@ -160,6 +194,8 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
? "range" ? "range"
: "min-range"; : "min-range";
const [sliderValue, uiValue] = this._genSliderValue(stateObj);
this._broadCard = this.clientWidth > 390;
this._jQuery("#thermostat", this.shadowRoot).roundSlider({ this._jQuery("#thermostat", this.shadowRoot).roundSlider({
...thermostatConfig, ...thermostatConfig,
radius: this.clientWidth / 3, radius: this.clientWidth / 3,
@ -168,18 +204,14 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
sliderType: _sliderType, sliderType: _sliderType,
change: (value) => this._setTemperature(value), change: (value) => this._setTemperature(value),
drag: (value) => this._dragEvent(value), drag: (value) => this._dragEvent(value),
value: sliderValue,
}); });
this._updateSetTemp(uiValue);
} }
protected updated(changedProps: PropertyValues): void { private _genSliderValue(stateObj: ClimateEntity): [string | number, string] {
if (!this._config || !this.hass || !this._jQuery) { let sliderValue: string | number;
return; let uiValue: string;
}
const stateObj = this.hass.states[this._config.entity] as ClimateEntity;
let sliderValue;
let uiValue;
if ( if (
stateObj.attributes.target_temp_low && stateObj.attributes.target_temp_low &&
@ -193,18 +225,11 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
String(stateObj.attributes.target_temp_high), String(stateObj.attributes.target_temp_high),
]); ]);
} else { } else {
sliderValue = uiValue = stateObj.attributes.temperature; sliderValue = stateObj.attributes.temperature;
uiValue = "" + stateObj.attributes.temperature;
} }
this._jQuery("#thermostat", this.shadowRoot).roundSlider({ return [sliderValue, uiValue];
value: sliderValue,
});
this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = uiValue;
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.themes !== this.hass.themes) {
applyThemesOnElement(this, this.hass.themes, this._config.theme);
}
} }
private renderStyle(): TemplateResult { private renderStyle(): TemplateResult {
@ -383,10 +408,12 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
`; `;
} }
private _updateSetTemp(value: string): void {
this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = value;
}
private _dragEvent(e): void { private _dragEvent(e): void {
this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = formatTemp( this._updateSetTemp(formatTemp(String(e.value).split(",")));
String(e.value).split(",")
);
} }
private _setTemperature(e): void { private _setTemperature(e): void {

View File

@ -44,6 +44,7 @@ import { HUIView } from "./hui-view";
import createCardElement from "./common/create-card-element"; import createCardElement from "./common/create-card-element";
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog"; import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
import { Lovelace } from "./types"; import { Lovelace } from "./types";
import { afterNextRender } from "../../common/util/render-status";
// CSS and JS should only be imported once. Modules and HTML are safe. // CSS and JS should only be imported once. Modules and HTML are safe.
const CSS_CACHE = {}; const CSS_CACHE = {};
@ -57,10 +58,11 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) {
public columns?: number; public columns?: number;
public route?: { path: string; prefix: string }; public route?: { path: string; prefix: string };
private _routeData?: { view: string }; private _routeData?: { view: string };
private _curView: number | "unused"; private _curView?: number | "unused";
private notificationsOpen?: boolean; private notificationsOpen?: boolean;
private _persistentNotifications?: Notification[]; private _persistentNotifications?: Notification[];
private _haStyle?: DocumentFragment; private _haStyle?: DocumentFragment;
private _viewCache?: { [viewId: string]: HUIView };
private _debouncedConfigChanged: () => void; private _debouncedConfigChanged: () => void;
private _unsubNotifications?: () => void; private _unsubNotifications?: () => void;
@ -82,9 +84,8 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) {
constructor() { constructor() {
super(); super();
this._curView = 0;
this._debouncedConfigChanged = debounce( this._debouncedConfigChanged = debounce(
() => this._selectView(this._curView), () => this._selectView(this._curView, true),
100 100
); );
} }
@ -349,6 +350,9 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) {
huiView.hass = this.hass; huiView.hass = this.hass;
} }
let newSelectView;
let force = false;
if (changedProperties.has("route")) { if (changedProperties.has("route")) {
const views = this.config && this.config.views; const views = this.config && this.config.views;
if ( if (
@ -367,9 +371,7 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) {
break; break;
} }
} }
if (index !== this._curView) { newSelectView = index;
this._selectView(index);
}
} }
} }
@ -379,15 +381,20 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) {
| undefined; | undefined;
if (!oldLovelace || oldLovelace.config !== this.lovelace!.config) { if (!oldLovelace || oldLovelace.config !== this.lovelace!.config) {
this._viewCache = {};
this._loadResources(this.lovelace!.config.resources || []); this._loadResources(this.lovelace!.config.resources || []);
// On config change, recreate the view from scratch. // On config change, recreate the view from scratch.
this._selectView(this._curView); force = true;
} }
if (!oldLovelace || oldLovelace.editMode !== this.lovelace!.editMode) { if (!oldLovelace || oldLovelace.editMode !== this.lovelace!.editMode) {
this._editModeChanged(); force = true;
} }
} }
if (newSelectView !== undefined || force) {
this._selectView(newSelectView, force);
}
} }
private get _notifications() { private get _notifications() {
@ -442,7 +449,7 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) {
} }
private _handleUnusedEntities(): void { private _handleUnusedEntities(): void {
this._selectView("unused"); this._selectView("unused", false);
} }
private _deselect(ev): void { private _deselect(ev): void {
@ -471,10 +478,6 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) {
} }
} }
private _editModeChanged(): void {
this._selectView(this._curView);
}
private _editView() { private _editView() {
showEditViewDialog(this, { showEditViewDialog(this, {
lovelace: this.lovelace!, lovelace: this.lovelace!,
@ -501,45 +504,61 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) {
scrollToTarget(this, this._layout.header.scrollTarget); scrollToTarget(this, this._layout.header.scrollTarget);
} }
private _selectView(viewIndex: HUIRoot["_curView"]): void { private async _selectView(
viewIndex: HUIRoot["_curView"],
force: boolean
): Promise<void> {
if (!force && this._curView === viewIndex) {
return;
}
viewIndex = viewIndex === undefined ? 0 : viewIndex;
this._curView = viewIndex; this._curView = viewIndex;
// Recreate a new element to clear the applied themes. // Recreate a new element to clear the applied themes.
const root = this._view; const root = this._view;
if (root.lastChild) { if (root.lastChild) {
root.removeChild(root.lastChild); root.removeChild(root.lastChild);
} }
let view;
let background = this.config.background || "";
if (viewIndex === "unused") { if (viewIndex === "unused") {
view = document.createElement("hui-unused-entities"); const unusedEntities = document.createElement("hui-unused-entities");
view.setConfig(this.config); unusedEntities.setConfig(this.config);
} else { root.style.background = this.config.background || "";
const viewConfig = this.config.views[this._curView]; root.appendChild(unusedEntities);
return;
}
let view;
const viewConfig = this.config.views[viewIndex];
if (!viewConfig) { if (!viewConfig) {
this._editModeEnable(); this._editModeEnable();
return; return;
} }
if (!force && this._viewCache![viewIndex]) {
view = this._viewCache![viewIndex];
} else {
await new Promise((resolve) => afterNextRender(resolve));
if (viewConfig.panel && viewConfig.cards && viewConfig.cards.length > 0) { if (viewConfig.panel && viewConfig.cards && viewConfig.cards.length > 0) {
view = createCardElement(viewConfig.cards[0]); view = createCardElement(viewConfig.cards[0]);
view.isPanel = true; view.isPanel = true;
} else { } else {
view = document.createElement("hui-view"); view = document.createElement("hui-view");
view.lovelace = this.lovelace; view.lovelace = this.lovelace;
view.config = viewConfig;
view.columns = this.columns; view.columns = this.columns;
view.index = viewIndex; view.index = viewIndex;
} }
if (viewConfig.background) { this._viewCache![viewIndex] = view;
background = viewConfig.background;
} }
}
this._view.style.background = background;
view.hass = this.hass; view.hass = this.hass;
root.style.background =
viewConfig.background || this.config.background || "";
root.appendChild(view); root.appendChild(view);
} }

View File

@ -24,6 +24,24 @@ import { showEditCardDialog } from "./editor/card-editor/show-edit-card-dialog";
let editCodeLoaded = false; let editCodeLoaded = false;
// Find column with < 5 entities, else column with lowest count
const getColumnIndex = (columnEntityCount: number[], size: number) => {
let minIndex = 0;
for (let i = 0; i < columnEntityCount.length; i++) {
if (columnEntityCount[i] < 5) {
minIndex = i;
break;
}
if (columnEntityCount[i] < columnEntityCount[minIndex]) {
minIndex = i;
}
}
columnEntityCount[minIndex] += size;
return minIndex;
};
export class HUIView extends hassLocalizeLitMixin(LitElement) { export class HUIView extends hassLocalizeLitMixin(LitElement) {
public hass?: HomeAssistant; public hass?: HomeAssistant;
public lovelace?: Lovelace; public lovelace?: Lovelace;
@ -245,28 +263,12 @@ export class HUIView extends hassLocalizeLitMixin(LitElement) {
columnEntityCount.push(0); columnEntityCount.push(0);
} }
// Find column with < 5 entities, else column with lowest count
function getColumnIndex(size) {
let minIndex = 0;
for (let i = 0; i < columnEntityCount.length; i++) {
if (columnEntityCount[i] < 5) {
minIndex = i;
break;
}
if (columnEntityCount[i] < columnEntityCount[minIndex]) {
minIndex = i;
}
}
columnEntityCount[minIndex] += size;
return minIndex;
}
elements.forEach((el, index) => { elements.forEach((el, index) => {
const cardSize = computeCardSize(el); const cardSize = computeCardSize(el);
// Element to append might be the wrapped card when we're editing. // Element to append might be the wrapped card when we're editing.
columns[getColumnIndex(cardSize)].push(elementsToAppend[index]); columns[getColumnIndex(columnEntityCount, cardSize)].push(
elementsToAppend[index]
);
}); });
// Remove empty columns // Remove empty columns