diff --git a/setup.py b/setup.py index c5f9e949f6..3ef904a8ab 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20211026.0", + version="20211027.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/frontend", author="The Home Assistant Authors", diff --git a/src/auth/ha-auth-flow.ts b/src/auth/ha-auth-flow.ts index ff0a31e0f9..aa02e61d58 100644 --- a/src/auth/ha-auth-flow.ts +++ b/src/auth/ha-auth-flow.ts @@ -1,4 +1,5 @@ import "@material/mwc-button"; +import { genClientId } from "home-assistant-js-websocket"; import { css, CSSResultGroup, @@ -8,18 +9,18 @@ import { TemplateResult, } from "lit"; import { property, state } from "lit/decorators"; +import "../components/ha-alert"; import "../components/ha-checkbox"; +import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data"; import "../components/ha-form/ha-form"; import "../components/ha-formfield"; import "../components/ha-markdown"; -import "../components/ha-alert"; import { AuthProvider } from "../data/auth"; import { DataEntryFlowStep, DataEntryFlowStepForm, } from "../data/data_entry_flow"; import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin"; -import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data"; import "./ha-password-manager-polyfill"; type State = "loading" | "error" | "step"; @@ -205,7 +206,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) { .computeError=${this._computeErrorCallback(step)} @value-changed=${this._stepDataChanged} > - ${this.clientId === window.location.origin && step.step_id !== "mfa" + ${this.clientId === genClientId() && step.step_id !== "mfa" ? html` - + ${item.state + ? html`` + : ""} ${item.name} `; } - const initials = computeInitials(this.person.name); + const initials = computeUserInitials(this.person.name); return html`
diff --git a/src/components/user/ha-user-badge.ts b/src/components/user/ha-user-badge.ts index dc1692c35c..eb589b467a 100644 --- a/src/components/user/ha-user-badge.ts +++ b/src/components/user/ha-user-badge.ts @@ -10,25 +10,9 @@ import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { styleMap } from "lit/directives/style-map"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; -import { User } from "../../data/user"; +import { computeUserInitials, User } from "../../data/user"; import { CurrentUser, HomeAssistant } from "../../types"; -export const computeInitials = (name: string) => { - if (!name) { - return "?"; - } - return ( - name - .trim() - // Split by space and take first 3 words - .split(" ") - .slice(0, 3) - // Of each word, take first letter - .map((s) => s.substr(0, 1)) - .join("") - ); -}; - @customElement("ha-user-badge") class UserBadge extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -75,7 +59,7 @@ class UserBadge extends LitElement { class="picture" >
`; } - const initials = computeInitials(this.user.name); + const initials = computeUserInitials(this.user.name); return html`
diff --git a/src/data/history.ts b/src/data/history.ts index fed6abaf89..843da23c62 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -74,6 +74,8 @@ export interface StatisticValue { export interface StatisticsMetaData { unit_of_measurement: string; statistic_id: string; + source: string; + name?: string | null; } export type StatisticsValidationResult = diff --git a/src/data/user.ts b/src/data/user.ts index 5fe1f4f259..7b108b3e7c 100644 --- a/src/data/user.ts +++ b/src/data/user.ts @@ -57,3 +57,19 @@ export const deleteUser = async (hass: HomeAssistant, userId: string) => type: "config/auth/delete", user_id: userId, }); + +export const computeUserInitials = (name: string) => { + if (!name) { + return "?"; + } + return ( + name + .trim() + // Split by space and take first 3 words + .split(" ") + .slice(0, 3) + // Of each word, take first letter + .map((s) => s.substr(0, 1)) + .join("") + ); +}; diff --git a/src/panels/config/energy/dialogs/dialog-energy-battery-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-battery-settings.ts index 12ad6a67cc..2f7477327b 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-battery-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-battery-settings.ts @@ -73,7 +73,6 @@ export class DialogEnergyBatterySettings .label=${this.hass.localize( "ui.panel.config.energy.battery.dialog.energy_into_battery" )} - entities-only @value-changed=${this._statisticToChanged} > @@ -85,7 +84,6 @@ export class DialogEnergyBatterySettings .label=${this.hass.localize( "ui.panel.config.energy.battery.dialog.energy_out_of_battery" )} - entities-only @value-changed=${this._statisticFromChanged} > diff --git a/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts index 4a7002a8e3..e0ac123065 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts @@ -74,7 +74,6 @@ export class DialogEnergyDeviceSettings .label=${this.hass.localize( "ui.panel.config.energy.device_consumption.dialog.device_consumption_energy" )} - entities-only @value-changed=${this._statisticChanged} > diff --git a/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts index e4a4bf98ce..fa5c9062f7 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts @@ -106,7 +106,6 @@ export class DialogEnergyGasSettings ? "kWh" : "m³" })`} - entities-only @value-changed=${this._statisticChanged} > diff --git a/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts index 822470debe..0bb1ebf34e 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts @@ -103,7 +103,6 @@ export class DialogEnergyGridFlowSettings .label=${this.hass.localize( `ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.energy_stat` )} - entities-only @value-changed=${this._statisticChanged} > diff --git a/src/panels/config/energy/dialogs/dialog-energy-solar-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-solar-settings.ts index bcb5786097..320f94b1c0 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-solar-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-solar-settings.ts @@ -85,7 +85,6 @@ export class DialogEnergySolarSettings .label=${this.hass.localize( "ui.panel.config.energy.solar.dialog.solar_production_energy" )} - entities-only @value-changed=${this._statisticChanged} > diff --git a/src/panels/developer-tools/statistics/developer-tools-statistics.ts b/src/panels/developer-tools/statistics/developer-tools-statistics.ts index 71963bfe6e..9c056b0fa5 100644 --- a/src/panels/developer-tools/statistics/developer-tools-statistics.ts +++ b/src/panels/developer-tools/statistics/developer-tools-statistics.ts @@ -50,21 +50,21 @@ class HaPanelDevStatistics extends LitElement { private _columns = memoizeOne( (localize): DataTableColumnContainer => ({ state: { - title: "Entity", + title: "Name", sortable: true, filterable: true, grows: true, template: (entityState, data: any) => html`${entityState ? computeStateName(entityState) - : data.statistic_id}`, + : data.name || data.statistic_id}`, }, statistic_id: { title: "Statistic id", sortable: true, filterable: true, hidden: this.narrow, - width: "30%", + width: "20%", }, unit_of_measurement: { title: "Unit", @@ -72,6 +72,12 @@ class HaPanelDevStatistics extends LitElement { filterable: true, width: "10%", }, + source: { + title: "Source", + sortable: true, + filterable: true, + width: "10%", + }, issues: { title: "Issue", sortable: true, @@ -146,6 +152,7 @@ class HaPanelDevStatistics extends LitElement { this._data.push({ statistic_id: statisticId, unit_of_measurement: "", + source: "", state: this.hass.states[statisticId], issues: issues[statisticId], }); diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 87d3d311ac..17519b4b52 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -15,7 +15,6 @@ import { formatTimeWithSeconds } from "../../common/datetime/format_time"; import { restoreScroll } from "../../common/decorators/restore-scroll"; import { fireEvent } from "../../common/dom/fire_event"; import { computeDomain } from "../../common/entity/compute_domain"; -import { domainIcon } from "../../common/entity/domain_icon"; import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl"; import "../../components/entity/state-badge"; import "../../components/ha-circular-progress"; @@ -151,12 +150,12 @@ class HaLogbook extends LitElement { html` ` : ""} diff --git a/src/panels/lovelace/cards/hui-picture-entity-card.ts b/src/panels/lovelace/cards/hui-picture-entity-card.ts index 2e079a46a9..087e5e5069 100644 --- a/src/panels/lovelace/cards/hui-picture-entity-card.ts +++ b/src/panels/lovelace/cards/hui-picture-entity-card.ts @@ -7,14 +7,12 @@ import { TemplateResult, } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { classMap } from "lit/directives/class-map"; import { ifDefined } from "lit/directives/if-defined"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateName } from "../../../common/entity/compute_state_name"; import "../../../components/ha-card"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; import { ActionHandlerEvent } from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; import { actionHandler } from "../common/directives/action-handler-directive"; @@ -135,9 +133,9 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
`; } else if (this._config.show_name) { - footer = html``; + footer = html``; } else if (this._config.show_state) { - footer = html``; + footer = html``; } return html` @@ -163,9 +161,6 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard { ? "0" : undefined )} - class=${classMap({ - clickable: !UNAVAILABLE_STATES.includes(stateObj.state), - })} > ${footer} @@ -182,7 +177,7 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard { box-sizing: border-box; } - hui-image.clickable { + hui-image { cursor: pointer; } @@ -212,8 +207,8 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard { justify-content: space-between; } - .state { - text-align: right; + .single { + text-align: center; } `; } diff --git a/src/panels/lovelace/cards/hui-statistics-graph-card.ts b/src/panels/lovelace/cards/hui-statistics-graph-card.ts index 01ac71e297..f82b650394 100644 --- a/src/panels/lovelace/cards/hui-statistics-graph-card.ts +++ b/src/panels/lovelace/cards/hui-statistics-graph-card.ts @@ -78,7 +78,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { } const configEntities = config.entities - ? processConfigEntities(config.entities) + ? processConfigEntities(config.entities, false) : []; this._entities = []; diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index 2c7b5d757e..0561b1c9db 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -16,6 +16,7 @@ import type { EntityRegistryEntry } from "../../../data/entity_registry"; import { domainToName } from "../../../data/integration"; import { LovelaceCardConfig, LovelaceViewConfig } from "../../../data/lovelace"; import { SENSOR_DEVICE_CLASS_BATTERY } from "../../../data/sensor"; +import { computeUserInitials } from "../../../data/user"; import { AlarmPanelCardConfig, EntitiesCardConfig, @@ -31,6 +32,8 @@ const HIDE_DOMAIN = new Set([ "device_tracker", "geo_location", "persistent_notification", + "script", + "sun", "zone", ]); @@ -230,6 +233,62 @@ export const generateViewConfig = ( let cards: LovelaceCardConfig[] = []; + if ("person" in ungroupedEntitites) { + const personCards: LovelaceCardConfig[] = []; + + if (ungroupedEntitites.person.length === 1) { + cards.push({ + type: "entities", + entities: ungroupedEntitites.person, + }); + } else { + let backgroundColor: string | undefined; + let foregroundColor = ""; + + for (const personEntityId of ungroupedEntitites.person) { + const stateObj = entities[personEntityId]; + + let image = stateObj.attributes.entity_picture; + + if (!image) { + if (backgroundColor === undefined) { + const computedStyle = getComputedStyle(document.body); + backgroundColor = encodeURIComponent( + computedStyle.getPropertyValue("--light-primary-color").trim() + ); + foregroundColor = encodeURIComponent( + ( + computedStyle.getPropertyValue("--text-light-primary-color") || + computedStyle.getPropertyValue("--primary-text-color") + ).trim() + ); + } + const initials = computeUserInitials( + stateObj.attributes.friendly_name || "" + ); + image = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 50 50' width='50' height='50' style='background-color:${backgroundColor}'%3E%3Cg%3E%3Ctext font-family='roboto' x='50%25' y='50%25' text-anchor='middle' stroke='${foregroundColor}' font-size='1.3em' dy='.3em'%3E${initials}%3C/text%3E%3C/g%3E%3C/svg%3E`; + } + + personCards.push({ + type: "picture-entity", + entity: personEntityId, + aspect_ratio: "1", + show_name: false, + image, + }); + } + + cards.push({ + type: "grid", + square: true, + columns: 3, + cards: personCards, + }); + } + + delete ungroupedEntitites.person; + } + splitted.groups.forEach((groupEntity) => { cards = cards.concat( computeCards( diff --git a/src/panels/lovelace/common/has-changed.ts b/src/panels/lovelace/common/has-changed.ts index 1675f49e20..a9d5fab797 100644 --- a/src/panels/lovelace/common/has-changed.ts +++ b/src/panels/lovelace/common/has-changed.ts @@ -52,7 +52,7 @@ export function hasConfigOrEntitiesChanged( const oldHass = changedProps.get("hass") as HomeAssistant; - const entities = processConfigEntities(element._config!.entities); + const entities = processConfigEntities(element._config!.entities, false); return entities.some( (entity) => diff --git a/src/panels/lovelace/common/process-config-entities.ts b/src/panels/lovelace/common/process-config-entities.ts index f17e2799d0..2dfa9a92a7 100644 --- a/src/panels/lovelace/common/process-config-entities.ts +++ b/src/panels/lovelace/common/process-config-entities.ts @@ -5,7 +5,8 @@ import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types"; export const processConfigEntities = < T extends EntityConfig | LovelaceRowConfig >( - entities: Array + entities: Array, + checkEntityId = true ): T[] => { if (!entities || !Array.isArray(entities)) { throw new Error("Entities need to be an array"); @@ -35,7 +36,7 @@ export const processConfigEntities = < throw new Error(`Invalid entity specified at position ${index}.`); } - if (!isValidEntityId((config as EntityConfig).entity!)) { + if (checkEntityId && !isValidEntityId((config as EntityConfig).entity!)) { throw new Error( `Invalid entity ID at position ${index}: ${ (config as EntityConfig).entity diff --git a/src/panels/lovelace/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts index d92a04790d..23510d29d3 100644 --- a/src/panels/lovelace/components/hui-image.ts +++ b/src/panels/lovelace/components/hui-image.ts @@ -192,7 +192,7 @@ export class HuiImage extends LitElement { : undefined, backgroundImage: useRatio && this._loadedImageSrc - ? `url(${this._loadedImageSrc})` + ? `url("${this._loadedImageSrc}")` : undefined, filter: this._loadState === LoadState.Loaded || this.cameraView === "live" diff --git a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts index 9eab0ed1d7..962d548981 100644 --- a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts @@ -61,7 +61,7 @@ export class HuiStatisticsGraphCardEditor assert(config, cardConfigStruct); this._config = config; this._configEntities = config.entities - ? processConfigEntities(config.entities).map((cfg) => cfg.entity) + ? processConfigEntities(config.entities, false).map((cfg) => cfg.entity) : []; }