Change entity badge display type to 3 booleans : name, state and icon (#21798)

* Change display type to 3 boolean : name, state and icon for entity badge

* Fix image url

* Fix not found entity

* Update state-label badge migration
This commit is contained in:
Paul Bottein 2024-08-28 09:53:07 +02:00 committed by GitHub
parent e9cbd54979
commit 7c5f947865
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 164 additions and 84 deletions

View File

@ -13,7 +13,7 @@ export const ensureBadgeConfig = (
return {
type: "entity",
entity: config,
display_type: "complete",
show_name: true,
};
}
if ("type" in config && config.type) {

View File

@ -1,3 +1,4 @@
import { mdiAlertCircle } from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
@ -5,15 +6,16 @@ import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { mdiAlertCircle } from "@mdi/js";
import { computeCssColor } from "../../../common/color/compute-color";
import { hsv2rgb, rgb2hex, rgb2hsv } from "../../../common/color/convert-color";
import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { stateActive } from "../../../common/entity/state_active";
import { stateColorCss } from "../../../common/entity/state_color";
import "../../../components/ha-ripple";
import "../../../components/ha-state-icon";
import "../../../components/ha-svg-icon";
import { cameraUrlWithWidthHeight } from "../../../data/camera";
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive";
@ -22,15 +24,38 @@ import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action";
import { LovelaceBadge, LovelaceBadgeEditor } from "../types";
import { EntityBadgeConfig } from "./types";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { cameraUrlWithWidthHeight } from "../../../data/camera";
export const DISPLAY_TYPES = ["minimal", "standard", "complete"] as const;
export type DisplayType = (typeof DISPLAY_TYPES)[number];
export const DEFAULT_DISPLAY_TYPE: DisplayType = "standard";
export const DEFAULT_CONFIG: EntityBadgeConfig = {
type: "entity",
show_name: false,
show_state: true,
show_icon: true,
};
export const migrateLegacyEntityBadgeConfig = (
config: EntityBadgeConfig
): EntityBadgeConfig => {
const newConfig = { ...config };
if (config.display_type) {
if (config.show_name === undefined) {
if (config.display_type === "complete") {
newConfig.show_name = true;
}
}
if (config.show_state === undefined) {
if (config.display_type === "minimal") {
newConfig.show_state = false;
}
}
delete newConfig.display_type;
}
return newConfig;
};
@customElement("hui-entity-badge")
export class HuiEntityBadge extends LitElement implements LovelaceBadge {
public static async getConfigElement(): Promise<LovelaceBadgeEditor> {
@ -64,7 +89,10 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
@state() protected _config?: EntityBadgeConfig;
public setConfig(config: EntityBadgeConfig): void {
this._config = config;
this._config = {
...DEFAULT_CONFIG,
...migrateLegacyEntityBadgeConfig(config),
};
}
get hasAction() {
@ -134,9 +162,9 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
return html`
<div class="badge error">
<ha-svg-icon .hass=${this.hass} .path=${mdiAlertCircle}></ha-svg-icon>
<span class="content">
<span class="name">${entityId}</span>
<span class="state">
<span class="info">
<span class="label">${entityId}</span>
<span class="content">
${this.hass.localize("ui.badge.entity.not_found")}
</span>
</span>
@ -163,18 +191,25 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
const name = this._config.name || stateObj.attributes.friendly_name;
const displayType = this._config.display_type || DEFAULT_DISPLAY_TYPE;
const showState = this._config.show_state;
const showName = this._config.show_name;
const showIcon = this._config.show_icon;
const showEntityPicture = this._config.show_entity_picture;
const imageUrl = this._config.show_entity_picture
const imageUrl = showEntityPicture
? this._getImageUrl(stateObj)
: undefined;
const label = showState && showName ? name : undefined;
const content = showState ? stateDisplay : showName ? name : undefined;
return html`
<div
style=${styleMap(style)}
class="badge ${classMap({
active,
[displayType]: true,
"no-info": !showState && !showName,
"no-icon": !showIcon,
})}"
@action=${this._handleAction}
.actionHandler=${actionHandler({
@ -185,22 +220,22 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
tabindex=${ifDefined(this.hasAction ? "0" : undefined)}
>
<ha-ripple .disabled=${!this.hasAction}></ha-ripple>
${imageUrl
? html`<img src=${imageUrl} aria-hidden />`
: html`
<ha-state-icon
.hass=${this.hass}
.stateObj=${stateObj}
.icon=${this._config.icon}
></ha-state-icon>
`}
${displayType !== "minimal"
${showIcon
? imageUrl
? html`<img src=${imageUrl} aria-hidden />`
: html`
<ha-state-icon
.hass=${this.hass}
.stateObj=${stateObj}
.icon=${this._config.icon}
></ha-state-icon>
`
: nothing}
${content
? html`
<span class="content">
${displayType === "complete"
? html`<span class="name">${name}</span>`
: nothing}
<span class="state">${stateDisplay}</span>
<span class="info">
${label ? html`<span class="label">${name}</span>` : nothing}
<span class="content">${content}</span>
</span>
`
: nothing}
@ -277,7 +312,7 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
.badge.active {
--badge-color: var(--primary-color);
}
.content {
.info {
display: flex;
flex-direction: column;
align-items: flex-start;
@ -285,7 +320,7 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
padding-inline-end: 4px;
padding-inline-start: initial;
}
.name {
.label {
font-size: 10px;
font-style: normal;
font-weight: 500;
@ -293,7 +328,7 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
letter-spacing: 0.1px;
color: var(--secondary-text-color);
}
.state {
.content {
font-size: 12px;
font-style: normal;
font-weight: 500;
@ -313,14 +348,20 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
object-fit: cover;
overflow: hidden;
}
.badge.minimal {
.badge.no-info {
padding: 0;
}
.badge:not(.minimal) img {
.badge:not(.no-icon) img {
margin-left: -6px;
margin-inline-start: -6px;
margin-inline-end: initial;
}
.badge.no-icon .info {
padding-right: 4px;
padding-left: 4px;
padding-inline-end: 4px;
padding-inline-start: 4px;
}
`;
}
}

View File

@ -16,10 +16,10 @@ export class HuiStateLabelBadge extends HuiEntityBadge {
const entityBadgeConfig: EntityBadgeConfig = {
type: "entity",
entity: config.entity,
display_type: config.show_name === false ? "standard" : "complete",
show_name: config.show_name ?? true,
};
this._config = entityBadgeConfig;
super.setConfig(entityBadgeConfig);
}
}

View File

@ -3,6 +3,7 @@ import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
import type { LegacyStateFilter } from "../common/evaluate-filter";
import type { Condition } from "../common/validate-condition";
import type { EntityFilterEntityConfig } from "../entity-rows/types";
import type { DisplayType } from "./hui-entity-badge";
export interface EntityFilterBadgeConfig extends LovelaceBadgeConfig {
type: "entity-filter";
@ -33,10 +34,16 @@ export interface EntityBadgeConfig extends LovelaceBadgeConfig {
name?: string;
icon?: string;
color?: string;
show_name?: boolean;
show_state?: boolean;
show_icon?: boolean;
show_entity_picture?: boolean;
display_type?: "minimal" | "standard" | "complete";
state_content?: string | string[];
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
/**
* @deprecated use `show_state`, `show_name`, `icon_type`
*/
display_type?: DisplayType;
}

View File

@ -22,8 +22,9 @@ import type {
} from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import {
DEFAULT_DISPLAY_TYPE,
DEFAULT_CONFIG,
DISPLAY_TYPES,
migrateLegacyEntityBadgeConfig,
} from "../../badges/hui-entity-badge";
import { EntityBadgeConfig } from "../../badges/types";
import type { LovelaceBadgeEditor } from "../../types";
@ -42,10 +43,12 @@ const badgeConfigStruct = assign(
icon: optional(string()),
state_content: optional(union([string(), array(string())])),
color: optional(string()),
show_name: optional(boolean()),
show_state: optional(boolean()),
show_icon: optional(boolean()),
show_entity_picture: optional(boolean()),
tap_action: optional(actionConfigStruct),
show_name: optional(boolean()),
image: optional(string()),
image: optional(string()), // For old badge config support
})
);
@ -60,7 +63,10 @@ export class HuiEntityBadgeEditor
public setConfig(config: EntityBadgeConfig): void {
assert(config, badgeConfigStruct);
this._config = config;
this._config = {
...DEFAULT_CONFIG,
...migrateLegacyEntityBadgeConfig(config),
};
}
private _schema = memoizeOne(
@ -73,20 +79,6 @@ export class HuiEntityBadgeEditor
iconPath: mdiPalette,
title: localize(`ui.panel.lovelace.editor.badge.entity.appearance`),
schema: [
{
name: "display_type",
selector: {
select: {
mode: "dropdown",
options: DISPLAY_TYPES.map((type) => ({
value: type,
label: localize(
`ui.panel.lovelace.editor.badge.entity.display_type_options.${type}`
),
})),
},
},
},
{
name: "",
type: "grid",
@ -97,6 +89,12 @@ export class HuiEntityBadgeEditor
text: {},
},
},
{
name: "color",
selector: {
ui_color: { default_color: true },
},
},
{
name: "icon",
selector: {
@ -104,12 +102,6 @@ export class HuiEntityBadgeEditor
},
context: { icon_entity: "entity" },
},
{
name: "color",
selector: {
ui_color: { default_color: true },
},
},
{
name: "show_entity_picture",
selector: {
@ -118,7 +110,35 @@ export class HuiEntityBadgeEditor
},
],
},
{
name: "displayed_elements",
selector: {
select: {
mode: "list",
multiple: true,
options: [
{
value: "name",
label: localize(
`ui.panel.lovelace.editor.badge.entity.displayed_elements_options.name`
),
},
{
value: "state",
label: localize(
`ui.panel.lovelace.editor.badge.entity.displayed_elements_options.state`
),
},
{
value: "icon",
label: localize(
`ui.panel.lovelace.editor.badge.entity.displayed_elements_options.icon`
),
},
],
},
},
},
{
name: "state_content",
selector: {
@ -151,6 +171,20 @@ export class HuiEntityBadgeEditor
] as const satisfies readonly HaFormSchema[]
);
_displayedElements = memoizeOne((config: EntityBadgeConfig) => {
const elements: string[] = [];
if (config.show_name) {
elements.push("name");
}
if (config.show_state) {
elements.push("state");
}
if (config.show_icon) {
elements.push("icon");
}
return elements;
});
protected render() {
if (!this.hass || !this._config) {
return nothing;
@ -158,11 +192,10 @@ export class HuiEntityBadgeEditor
const schema = this._schema(this.hass!.localize);
const data = { ...this._config };
if (!data.display_type) {
data.display_type = DEFAULT_DISPLAY_TYPE;
}
const data = {
...this._config,
displayed_elements: this._displayedElements(this._config),
};
return html`
<ha-form
@ -181,18 +214,17 @@ export class HuiEntityBadgeEditor
return;
}
const newConfig = ev.detail.value as EntityBadgeConfig;
const config: EntityBadgeConfig = {
...newConfig,
};
const config = { ...ev.detail.value } as EntityBadgeConfig;
if (!config.state_content) {
delete config.state_content;
}
if (config.display_type === "standard") {
delete config.display_type;
if (config.displayed_elements) {
config.show_name = config.displayed_elements.includes("name");
config.show_state = config.displayed_elements.includes("state");
config.show_icon = config.displayed_elements.includes("icon");
delete config.displayed_elements;
}
fireEvent(this, "config-changed", { config });
@ -204,8 +236,8 @@ export class HuiEntityBadgeEditor
switch (schema.name) {
case "color":
case "state_content":
case "display_type":
case "show_entity_picture":
case "displayed_elements":
return this.hass!.localize(
`ui.panel.lovelace.editor.badge.entity.${schema.name}`
);

View File

@ -30,11 +30,11 @@ export class HuiStateLabelBadgeEditor extends HuiEntityBadgeEditor {
const entityBadgeConfig: EntityBadgeConfig = {
type: "entity",
entity: config.entity,
display_type: config.show_name === false ? "standard" : "complete",
show_name: config.show_name ?? true,
};
// @ts-ignore
this._config = entityBadgeConfig;
super.setConfig(entityBadgeConfig);
}
}

View File

@ -5959,9 +5959,9 @@
"paste": "Paste from clipboard",
"paste_description": "Paste a {type} card from the clipboard",
"refresh_interval": "Refresh interval",
"show_icon": "Show icon?",
"show_name": "Show name?",
"show_state": "Show state?",
"show_icon": "Show icon",
"show_name": "Show name",
"show_state": "Show state",
"tap_action": "Tap behavior",
"title": "Title",
"theme": "Theme",
@ -6101,11 +6101,11 @@
"appearance": "Appearance",
"show_entity_picture": "Show entity picture",
"state_content": "State content",
"display_type": "Display type",
"display_type_options": {
"minimal": "Minimal (icon only)",
"standard": "Standard (icon and state)",
"complete": "Complete (icon, name and state)"
"displayed_elements": "Displayed elements",
"displayed_elements_options": {
"icon": "Icon",
"name": "Name",
"state": "State"
}
},
"generic": {