First commit

This commit is contained in:
Zack Arnett 2021-11-16 19:34:31 -06:00
parent c238c7dbbc
commit 326d912b84
5 changed files with 319 additions and 0 deletions

View File

@ -0,0 +1,200 @@
import { mdiBattery70 } from "@mdi/js";
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { computeStateName } from "../../../common/entity/compute_state_name";
import "../../../components/ha-card";
import "../../../components/ha-svg-icon";
import { computeUserInitials } from "../../../data/user";
import { HomeAssistant } from "../../../types";
// eslint-disable-next-line import/no-duplicates
import "../components/hui-warning";
// eslint-disable-next-line import/no-duplicates
import { createEntityNotFoundWarning } from "../components/hui-warning";
import { LovelaceCard, LovelaceCardEditor } from "../types";
import { PersonCardConfig } from "./types";
@customElement("hui-person-card")
export class HuiPersonCard extends LitElement implements LovelaceCard {
public static async getConfigElement(): Promise<LovelaceCardEditor> {
await import("../editor/config-elements/hui-person-card-editor");
return document.createElement("hui-person-card-editor");
}
public static getStubConfig(): PersonCardConfig {
return {
type: "person",
};
}
@property({ attribute: false }) public hass?: HomeAssistant;
@state() protected _config?: PersonCardConfig;
public getCardSize(): number {
return 5;
}
public setConfig(config: PersonCardConfig): void {
if (!config || !config.entity) {
throw new Error("Entity required");
}
this._config = config;
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
if (changedProps.size === 1 && changedProps.has("hass")) {
return !changedProps.get("hass");
}
return true;
}
protected updated(changedProps: PropertyValues): void {
super.updated(changedProps);
if (!this._config || !this.hass) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
const oldConfig = changedProps.get("_config") as
| PersonCardConfig
| undefined;
if (
!oldHass ||
!oldConfig ||
oldHass.themes !== this.hass.themes ||
oldConfig.theme !== this._config.theme
) {
applyThemesOnElement(this, this.hass.themes, this._config.theme);
}
}
protected render(): TemplateResult {
if (!this._config || !this.hass) {
return html``;
}
const stateObj = this.hass.states[this._config.entity!];
if (!stateObj) {
return html`
<hui-warning>
${createEntityNotFoundWarning(this.hass, this._config.entity!)}
</hui-warning>
`;
}
let backgroundColor: string | undefined;
let foregroundColor = "";
let image = stateObj.attributes.entity_picture;
let batteryPercent: number | undefined;
if (stateObj.attributes.source) {
batteryPercent =
this.hass.states[stateObj.attributes.source].attributes.battery;
}
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`;
}
return html`
<ha-card>
<hui-image .hass=${this.hass} .image=${image}></hui-image>
<div class="name">${computeStateName(stateObj)}</div>
<div class="state">
${computeStateDisplay(
this.hass!.localize,
stateObj,
this.hass.locale
)}
</div>
<div class="battery">
<ha-svg-icon .path=${mdiBattery70}></ha-svg-icon> ${batteryPercent} %
</div>
</ha-card>
`;
}
static get styles(): CSSResultGroup {
return css`
ha-card {
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 16px;
}
ha-card.clickable {
cursor: pointer;
}
hui-image {
display: block;
width: 100%;
max-width: 250px;
border-radius: 50%;
overflow: hidden;
}
.name {
font-size: 24px;
padding-top: 16px;
font-weight: 500;
text-align: center;
}
.state {
color: var(--disabled-text-color);
padding-top: 24px;
}
.battery {
border: 1px solid var(--divider-color);
border-radius: 4px;
padding: 4px;
font-size: 16px;
background-color: var(
--ha-card-background,
var(--card-background-color, white)
);
display: flex;
justify-content: center;
align-items: center;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-person-card": HuiPersonCard;
}
}

View File

@ -288,6 +288,10 @@ export interface PictureCardConfig extends LovelaceCardConfig {
double_tap_action?: ActionConfig; double_tap_action?: ActionConfig;
theme?: string; theme?: string;
} }
export interface PersonCardConfig extends LovelaceCardConfig {
entity?: string;
theme?: string;
}
export interface PictureElementsCardConfig extends LovelaceCardConfig { export interface PictureElementsCardConfig extends LovelaceCardConfig {
title?: string; title?: string;

View File

@ -59,6 +59,7 @@ const LAZY_LOAD_TYPES = {
"entity-filter": () => import("../cards/hui-entity-filter-card"), "entity-filter": () => import("../cards/hui-entity-filter-card"),
humidifier: () => import("../cards/hui-humidifier-card"), humidifier: () => import("../cards/hui-humidifier-card"),
"media-control": () => import("../cards/hui-media-control-card"), "media-control": () => import("../cards/hui-media-control-card"),
person: () => import("../cards/hui-person-card"),
"picture-elements": () => import("../cards/hui-picture-elements-card"), "picture-elements": () => import("../cards/hui-picture-elements-card"),
"picture-entity": () => import("../cards/hui-picture-entity-card"), "picture-entity": () => import("../cards/hui-picture-entity-card"),
"picture-glance": () => import("../cards/hui-picture-glance-card"), "picture-glance": () => import("../cards/hui-picture-glance-card"),

View File

@ -0,0 +1,110 @@
import "@polymer/paper-input/paper-input";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { assert, assign, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-icon-picker";
import { HomeAssistant } from "../../../../types";
import { PersonCardConfig } from "../../cards/types";
import "../../components/hui-action-editor";
import "../../components/hui-entity-editor";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { EditorTarget } from "../types";
import { configElementStyle } from "./config-elements-style";
const cardConfigStruct = assign(
baseLovelaceCardConfig,
object({
entity: optional(string()),
theme: optional(string()),
})
);
@customElement("hui-person-card-editor")
export class HuiPersonCardEditor
extends LitElement
implements LovelaceCardEditor
{
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _config?: PersonCardConfig;
public setConfig(config: PersonCardConfig): void {
assert(config, cardConfigStruct);
this._config = config;
}
get _theme(): string {
return this._config!.theme || "";
}
get _entity(): string {
return this._config!.entity || "";
}
protected render(): TemplateResult {
if (!this.hass || !this._config) {
return html``;
}
return html`
<div class="card-config">
<ha-entity-picker
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
.configValue=${"entity"}
@value-changed=${this._valueChanged}
allow-custom-entity
></ha-entity-picker>
<hui-theme-select-editor
.hass=${this.hass}
.value=${this._theme}
.configValue=${"theme"}
@value-changed=${this._valueChanged}
></hui-theme-select-editor>
</div>
`;
}
private _valueChanged(ev: CustomEvent): void {
if (!this._config || !this.hass) {
return;
}
const target = ev.target! as EditorTarget;
const value = ev.detail.value;
if (this[`_${target.configValue}`] === value) {
return;
}
if (target.configValue) {
if (value !== false && !value) {
this._config = { ...this._config };
delete this._config[target.configValue!];
} else {
this._config = {
...this._config,
[target.configValue!]: value,
};
}
}
fireEvent(this, "config-changed", { config: this._config });
}
static get styles(): CSSResultGroup {
return configElementStyle;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-person-card-editor": HuiPersonCardEditor;
}
}

View File

@ -57,6 +57,10 @@ export const coreCards: Card[] = [
type: "media-control", type: "media-control",
showElement: true, showElement: true,
}, },
{
type: "person",
showElement: true,
},
{ {
type: "picture", type: "picture",
showElement: true, showElement: true,