mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-12 11:56:34 +00:00
Merge pull request #10426 from home-assistant/dev
This commit is contained in:
commit
e3c0530941
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="home-assistant-frontend",
|
name="home-assistant-frontend",
|
||||||
version="20211026.0",
|
version="20211027.0",
|
||||||
description="The Home Assistant frontend",
|
description="The Home Assistant frontend",
|
||||||
url="https://github.com/home-assistant/frontend",
|
url="https://github.com/home-assistant/frontend",
|
||||||
author="The Home Assistant Authors",
|
author="The Home Assistant Authors",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
|
import { genClientId } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@ -8,18 +9,18 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
|
import "../components/ha-alert";
|
||||||
import "../components/ha-checkbox";
|
import "../components/ha-checkbox";
|
||||||
|
import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data";
|
||||||
import "../components/ha-form/ha-form";
|
import "../components/ha-form/ha-form";
|
||||||
import "../components/ha-formfield";
|
import "../components/ha-formfield";
|
||||||
import "../components/ha-markdown";
|
import "../components/ha-markdown";
|
||||||
import "../components/ha-alert";
|
|
||||||
import { AuthProvider } from "../data/auth";
|
import { AuthProvider } from "../data/auth";
|
||||||
import {
|
import {
|
||||||
DataEntryFlowStep,
|
DataEntryFlowStep,
|
||||||
DataEntryFlowStepForm,
|
DataEntryFlowStepForm,
|
||||||
} from "../data/data_entry_flow";
|
} from "../data/data_entry_flow";
|
||||||
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
||||||
import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data";
|
|
||||||
import "./ha-password-manager-polyfill";
|
import "./ha-password-manager-polyfill";
|
||||||
|
|
||||||
type State = "loading" | "error" | "step";
|
type State = "loading" | "error" | "step";
|
||||||
@ -205,7 +206,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
.computeError=${this._computeErrorCallback(step)}
|
.computeError=${this._computeErrorCallback(step)}
|
||||||
@value-changed=${this._stepDataChanged}
|
@value-changed=${this._stepDataChanged}
|
||||||
></ha-form>
|
></ha-form>
|
||||||
${this.clientId === window.location.origin && step.step_id !== "mfa"
|
${this.clientId === genClientId() && step.step_id !== "mfa"
|
||||||
? html`
|
? html`
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
class="store-token"
|
class="store-token"
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
/** An empty image which can be set as src of an img element. */
|
/** An empty image which can be set as src of an img element. */
|
||||||
export default "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
export const emptyImageBase64 =
|
||||||
|
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||||
|
@ -24,6 +24,7 @@ const BINARY_SENSOR_DEVICE_CLASS_COLOR_NOT_INVERTED = new Set([
|
|||||||
"plug",
|
"plug",
|
||||||
"power",
|
"power",
|
||||||
"presence",
|
"presence",
|
||||||
|
"running",
|
||||||
"update",
|
"update",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -102,7 +102,12 @@ export class HaStatisticPicker extends LitElement {
|
|||||||
</style>
|
</style>
|
||||||
<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>
|
||||||
<paper-icon-item>
|
<paper-icon-item>
|
||||||
<state-badge slot="item-icon" .stateObj=${item.state}></state-badge>
|
${item.state
|
||||||
|
? html`<state-badge
|
||||||
|
slot="item-icon"
|
||||||
|
.stateObj=${item.state}
|
||||||
|
></state-badge>`
|
||||||
|
: ""}
|
||||||
<paper-item-body two-line="">
|
<paper-item-body two-line="">
|
||||||
${item.name}
|
${item.name}
|
||||||
<span secondary
|
<span secondary
|
||||||
@ -153,7 +158,10 @@ export class HaStatisticPicker extends LitElement {
|
|||||||
const entityState = this.hass.states[meta.statistic_id];
|
const entityState = this.hass.states[meta.statistic_id];
|
||||||
if (!entityState) {
|
if (!entityState) {
|
||||||
if (!entitiesOnly) {
|
if (!entitiesOnly) {
|
||||||
output.push({ id: meta.statistic_id, name: meta.statistic_id });
|
output.push({
|
||||||
|
id: meta.statistic_id,
|
||||||
|
name: meta.name || meta.statistic_id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { customElement, property } from "lit/decorators";
|
|||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { Person } from "../../data/person";
|
import { Person } from "../../data/person";
|
||||||
import { computeInitials } from "./ha-user-badge";
|
import { computeUserInitials } from "../../data/user";
|
||||||
|
|
||||||
@customElement("ha-person-badge")
|
@customElement("ha-person-badge")
|
||||||
class PersonBadge extends LitElement {
|
class PersonBadge extends LitElement {
|
||||||
@ -22,7 +22,7 @@ class PersonBadge extends LitElement {
|
|||||||
class="picture"
|
class="picture"
|
||||||
></div>`;
|
></div>`;
|
||||||
}
|
}
|
||||||
const initials = computeInitials(this.person.name);
|
const initials = computeUserInitials(this.person.name);
|
||||||
return html`<div
|
return html`<div
|
||||||
class="initials ${classMap({ long: initials!.length > 2 })}"
|
class="initials ${classMap({ long: initials!.length > 2 })}"
|
||||||
>
|
>
|
||||||
|
@ -10,25 +10,9 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
||||||
import { User } from "../../data/user";
|
import { computeUserInitials, User } from "../../data/user";
|
||||||
import { CurrentUser, HomeAssistant } from "../../types";
|
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")
|
@customElement("ha-user-badge")
|
||||||
class UserBadge extends LitElement {
|
class UserBadge extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -75,7 +59,7 @@ class UserBadge extends LitElement {
|
|||||||
class="picture"
|
class="picture"
|
||||||
></div>`;
|
></div>`;
|
||||||
}
|
}
|
||||||
const initials = computeInitials(this.user.name);
|
const initials = computeUserInitials(this.user.name);
|
||||||
return html`<div
|
return html`<div
|
||||||
class="initials ${classMap({ long: initials!.length > 2 })}"
|
class="initials ${classMap({ long: initials!.length > 2 })}"
|
||||||
>
|
>
|
||||||
|
@ -74,6 +74,8 @@ export interface StatisticValue {
|
|||||||
export interface StatisticsMetaData {
|
export interface StatisticsMetaData {
|
||||||
unit_of_measurement: string;
|
unit_of_measurement: string;
|
||||||
statistic_id: string;
|
statistic_id: string;
|
||||||
|
source: string;
|
||||||
|
name?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StatisticsValidationResult =
|
export type StatisticsValidationResult =
|
||||||
|
@ -57,3 +57,19 @@ export const deleteUser = async (hass: HomeAssistant, userId: string) =>
|
|||||||
type: "config/auth/delete",
|
type: "config/auth/delete",
|
||||||
user_id: userId,
|
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("")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -73,7 +73,6 @@ export class DialogEnergyBatterySettings
|
|||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.energy.battery.dialog.energy_into_battery"
|
"ui.panel.config.energy.battery.dialog.energy_into_battery"
|
||||||
)}
|
)}
|
||||||
entities-only
|
|
||||||
@value-changed=${this._statisticToChanged}
|
@value-changed=${this._statisticToChanged}
|
||||||
></ha-statistic-picker>
|
></ha-statistic-picker>
|
||||||
|
|
||||||
@ -85,7 +84,6 @@ export class DialogEnergyBatterySettings
|
|||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.energy.battery.dialog.energy_out_of_battery"
|
"ui.panel.config.energy.battery.dialog.energy_out_of_battery"
|
||||||
)}
|
)}
|
||||||
entities-only
|
|
||||||
@value-changed=${this._statisticFromChanged}
|
@value-changed=${this._statisticFromChanged}
|
||||||
></ha-statistic-picker>
|
></ha-statistic-picker>
|
||||||
|
|
||||||
|
@ -74,7 +74,6 @@ export class DialogEnergyDeviceSettings
|
|||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.energy.device_consumption.dialog.device_consumption_energy"
|
"ui.panel.config.energy.device_consumption.dialog.device_consumption_energy"
|
||||||
)}
|
)}
|
||||||
entities-only
|
|
||||||
@value-changed=${this._statisticChanged}
|
@value-changed=${this._statisticChanged}
|
||||||
></ha-statistic-picker>
|
></ha-statistic-picker>
|
||||||
|
|
||||||
|
@ -106,7 +106,6 @@ export class DialogEnergyGasSettings
|
|||||||
? "kWh"
|
? "kWh"
|
||||||
: "m³"
|
: "m³"
|
||||||
})`}
|
})`}
|
||||||
entities-only
|
|
||||||
@value-changed=${this._statisticChanged}
|
@value-changed=${this._statisticChanged}
|
||||||
></ha-statistic-picker>
|
></ha-statistic-picker>
|
||||||
|
|
||||||
|
@ -103,7 +103,6 @@ export class DialogEnergyGridFlowSettings
|
|||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.energy_stat`
|
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.energy_stat`
|
||||||
)}
|
)}
|
||||||
entities-only
|
|
||||||
@value-changed=${this._statisticChanged}
|
@value-changed=${this._statisticChanged}
|
||||||
></ha-statistic-picker>
|
></ha-statistic-picker>
|
||||||
|
|
||||||
|
@ -85,7 +85,6 @@ export class DialogEnergySolarSettings
|
|||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.energy.solar.dialog.solar_production_energy"
|
"ui.panel.config.energy.solar.dialog.solar_production_energy"
|
||||||
)}
|
)}
|
||||||
entities-only
|
|
||||||
@value-changed=${this._statisticChanged}
|
@value-changed=${this._statisticChanged}
|
||||||
></ha-statistic-picker>
|
></ha-statistic-picker>
|
||||||
|
|
||||||
|
@ -50,21 +50,21 @@ class HaPanelDevStatistics extends LitElement {
|
|||||||
private _columns = memoizeOne(
|
private _columns = memoizeOne(
|
||||||
(localize): DataTableColumnContainer => ({
|
(localize): DataTableColumnContainer => ({
|
||||||
state: {
|
state: {
|
||||||
title: "Entity",
|
title: "Name",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
grows: true,
|
grows: true,
|
||||||
template: (entityState, data: any) =>
|
template: (entityState, data: any) =>
|
||||||
html`${entityState
|
html`${entityState
|
||||||
? computeStateName(entityState)
|
? computeStateName(entityState)
|
||||||
: data.statistic_id}`,
|
: data.name || data.statistic_id}`,
|
||||||
},
|
},
|
||||||
statistic_id: {
|
statistic_id: {
|
||||||
title: "Statistic id",
|
title: "Statistic id",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
hidden: this.narrow,
|
hidden: this.narrow,
|
||||||
width: "30%",
|
width: "20%",
|
||||||
},
|
},
|
||||||
unit_of_measurement: {
|
unit_of_measurement: {
|
||||||
title: "Unit",
|
title: "Unit",
|
||||||
@ -72,6 +72,12 @@ class HaPanelDevStatistics extends LitElement {
|
|||||||
filterable: true,
|
filterable: true,
|
||||||
width: "10%",
|
width: "10%",
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
title: "Source",
|
||||||
|
sortable: true,
|
||||||
|
filterable: true,
|
||||||
|
width: "10%",
|
||||||
|
},
|
||||||
issues: {
|
issues: {
|
||||||
title: "Issue",
|
title: "Issue",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
@ -146,6 +152,7 @@ class HaPanelDevStatistics extends LitElement {
|
|||||||
this._data.push({
|
this._data.push({
|
||||||
statistic_id: statisticId,
|
statistic_id: statisticId,
|
||||||
unit_of_measurement: "",
|
unit_of_measurement: "",
|
||||||
|
source: "",
|
||||||
state: this.hass.states[statisticId],
|
state: this.hass.states[statisticId],
|
||||||
issues: issues[statisticId],
|
issues: issues[statisticId],
|
||||||
});
|
});
|
||||||
|
@ -15,7 +15,6 @@ import { formatTimeWithSeconds } from "../../common/datetime/format_time";
|
|||||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { domainIcon } from "../../common/entity/domain_icon";
|
|
||||||
import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl";
|
import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl";
|
||||||
import "../../components/entity/state-badge";
|
import "../../components/entity/state-badge";
|
||||||
import "../../components/ha-circular-progress";
|
import "../../components/ha-circular-progress";
|
||||||
@ -151,12 +150,12 @@ class HaLogbook extends LitElement {
|
|||||||
html`
|
html`
|
||||||
<state-badge
|
<state-badge
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.overrideIcon=${item.icon ??
|
.overrideIcon=${item.icon}
|
||||||
domainIcon(domain, stateObj, item.state)}
|
|
||||||
.overrideImage=${DOMAINS_WITH_DYNAMIC_PICTURE.has(domain)
|
.overrideImage=${DOMAINS_WITH_DYNAMIC_PICTURE.has(domain)
|
||||||
? ""
|
? ""
|
||||||
: stateObj?.attributes.entity_picture_local ||
|
: stateObj?.attributes.entity_picture_local ||
|
||||||
stateObj?.attributes.entity_picture}
|
stateObj?.attributes.entity_picture}
|
||||||
|
.stateObj=${stateObj}
|
||||||
></state-badge>
|
></state-badge>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
@ -7,14 +7,12 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import { UNAVAILABLE_STATES } from "../../../data/entity";
|
|
||||||
import { ActionHandlerEvent } from "../../../data/lovelace";
|
import { ActionHandlerEvent } from "../../../data/lovelace";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
@ -135,9 +133,9 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} else if (this._config.show_name) {
|
} else if (this._config.show_name) {
|
||||||
footer = html`<div class="footer">${name}</div>`;
|
footer = html`<div class="footer single">${name}</div>`;
|
||||||
} else if (this._config.show_state) {
|
} else if (this._config.show_state) {
|
||||||
footer = html`<div class="footer state">${entityState}</div>`;
|
footer = html`<div class="footer single">${entityState}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@ -163,9 +161,6 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
|||||||
? "0"
|
? "0"
|
||||||
: undefined
|
: undefined
|
||||||
)}
|
)}
|
||||||
class=${classMap({
|
|
||||||
clickable: !UNAVAILABLE_STATES.includes(stateObj.state),
|
|
||||||
})}
|
|
||||||
></hui-image>
|
></hui-image>
|
||||||
${footer}
|
${footer}
|
||||||
</ha-card>
|
</ha-card>
|
||||||
@ -182,7 +177,7 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
hui-image.clickable {
|
hui-image {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,8 +207,8 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.state {
|
.single {
|
||||||
text-align: right;
|
text-align: center;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const configEntities = config.entities
|
const configEntities = config.entities
|
||||||
? processConfigEntities(config.entities)
|
? processConfigEntities(config.entities, false)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
this._entities = [];
|
this._entities = [];
|
||||||
|
@ -16,6 +16,7 @@ import type { EntityRegistryEntry } from "../../../data/entity_registry";
|
|||||||
import { domainToName } from "../../../data/integration";
|
import { domainToName } from "../../../data/integration";
|
||||||
import { LovelaceCardConfig, LovelaceViewConfig } from "../../../data/lovelace";
|
import { LovelaceCardConfig, LovelaceViewConfig } from "../../../data/lovelace";
|
||||||
import { SENSOR_DEVICE_CLASS_BATTERY } from "../../../data/sensor";
|
import { SENSOR_DEVICE_CLASS_BATTERY } from "../../../data/sensor";
|
||||||
|
import { computeUserInitials } from "../../../data/user";
|
||||||
import {
|
import {
|
||||||
AlarmPanelCardConfig,
|
AlarmPanelCardConfig,
|
||||||
EntitiesCardConfig,
|
EntitiesCardConfig,
|
||||||
@ -31,6 +32,8 @@ const HIDE_DOMAIN = new Set([
|
|||||||
"device_tracker",
|
"device_tracker",
|
||||||
"geo_location",
|
"geo_location",
|
||||||
"persistent_notification",
|
"persistent_notification",
|
||||||
|
"script",
|
||||||
|
"sun",
|
||||||
"zone",
|
"zone",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -230,6 +233,62 @@ export const generateViewConfig = (
|
|||||||
|
|
||||||
let cards: LovelaceCardConfig[] = [];
|
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) => {
|
splitted.groups.forEach((groupEntity) => {
|
||||||
cards = cards.concat(
|
cards = cards.concat(
|
||||||
computeCards(
|
computeCards(
|
||||||
|
@ -52,7 +52,7 @@ export function hasConfigOrEntitiesChanged(
|
|||||||
|
|
||||||
const oldHass = changedProps.get("hass") as HomeAssistant;
|
const oldHass = changedProps.get("hass") as HomeAssistant;
|
||||||
|
|
||||||
const entities = processConfigEntities(element._config!.entities);
|
const entities = processConfigEntities(element._config!.entities, false);
|
||||||
|
|
||||||
return entities.some(
|
return entities.some(
|
||||||
(entity) =>
|
(entity) =>
|
||||||
|
@ -5,7 +5,8 @@ import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types";
|
|||||||
export const processConfigEntities = <
|
export const processConfigEntities = <
|
||||||
T extends EntityConfig | LovelaceRowConfig
|
T extends EntityConfig | LovelaceRowConfig
|
||||||
>(
|
>(
|
||||||
entities: Array<T | string>
|
entities: Array<T | string>,
|
||||||
|
checkEntityId = true
|
||||||
): T[] => {
|
): T[] => {
|
||||||
if (!entities || !Array.isArray(entities)) {
|
if (!entities || !Array.isArray(entities)) {
|
||||||
throw new Error("Entities need to be an array");
|
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}.`);
|
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(
|
throw new Error(
|
||||||
`Invalid entity ID at position ${index}: ${
|
`Invalid entity ID at position ${index}: ${
|
||||||
(config as EntityConfig).entity
|
(config as EntityConfig).entity
|
||||||
|
@ -192,7 +192,7 @@ export class HuiImage extends LitElement {
|
|||||||
: undefined,
|
: undefined,
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
useRatio && this._loadedImageSrc
|
useRatio && this._loadedImageSrc
|
||||||
? `url(${this._loadedImageSrc})`
|
? `url("${this._loadedImageSrc}")`
|
||||||
: undefined,
|
: undefined,
|
||||||
filter:
|
filter:
|
||||||
this._loadState === LoadState.Loaded || this.cameraView === "live"
|
this._loadState === LoadState.Loaded || this.cameraView === "live"
|
||||||
|
@ -61,7 +61,7 @@ export class HuiStatisticsGraphCardEditor
|
|||||||
assert(config, cardConfigStruct);
|
assert(config, cardConfigStruct);
|
||||||
this._config = config;
|
this._config = config;
|
||||||
this._configEntities = config.entities
|
this._configEntities = config.entities
|
||||||
? processConfigEntities(config.entities).map((cfg) => cfg.entity)
|
? processConfigEntities(config.entities, false).map((cfg) => cfg.entity)
|
||||||
: [];
|
: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user