diff --git a/src/common/empty_image_base64.ts b/src/common/empty_image_base64.ts
index 2eb8cb22f..5c63dca0d 100644
--- a/src/common/empty_image_base64.ts
+++ b/src/common/empty_image_base64.ts
@@ -1,2 +1,3 @@
/** 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";
diff --git a/src/components/user/ha-person-badge.ts b/src/components/user/ha-person-badge.ts
index 9074ea7c9..043402599 100644
--- a/src/components/user/ha-person-badge.ts
+++ b/src/components/user/ha-person-badge.ts
@@ -3,7 +3,7 @@ import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { Person } from "../../data/person";
-import { computeInitials } from "./ha-user-badge";
+import { computeUserInitials } from "../../data/user";
@customElement("ha-person-badge")
class PersonBadge extends LitElement {
@@ -22,7 +22,7 @@ class PersonBadge extends LitElement {
class="picture"
>`;
}
- 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 dc1692c35..eb589b467 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/user.ts b/src/data/user.ts
index 5fe1f4f25..7b108b3e7 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/lovelace/cards/hui-picture-entity-card.ts b/src/panels/lovelace/cards/hui-picture-entity-card.ts
index 2e079a46a..087e5e506 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/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts
index aa61c01a7..0561b1c9d 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,
@@ -232,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/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts
index d92a04790..23510d29d 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"