Improve icon translations support (#19516)

* Use slot for icon picker

* Use icon translations for disabled entities in device page

* Don't use fallback for light effect

* Fix device class

* Fix climate hvac mode

* Migrate fan direction to icon translations

* Remove attribute fallback

* Rename variable

* Update src/panels/lovelace/card-features/hui-climate-hvac-modes-card-feature.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Paul Bottein 2024-01-24 18:31:06 +01:00 committed by GitHub
parent beb3454f8d
commit 85f086d02e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 237 additions and 388 deletions

View File

@ -1,46 +0,0 @@
/** Return an icon representing a attribute. */
import { mdiCircleMedium, mdiCreation } from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
import {
computeFanModeIcon,
computeHvacModeIcon,
computePresetModeIcon,
computeSwingModeIcon,
} from "../../data/climate";
import { computeHumidiferModeIcon } from "../../data/humidifier";
import { computeDomain } from "./compute_domain";
const iconGenerators: Record<string, Record<string, (value: any) => string>> = {
climate: {
fan_mode: computeFanModeIcon,
hvac_mode: computeHvacModeIcon,
preset_mode: computePresetModeIcon,
swing_mode: computeSwingModeIcon,
},
humidifier: {
mode: computeHumidiferModeIcon,
},
light: {
effect: () => mdiCreation,
},
fan: {
preset_mode: () => mdiCircleMedium,
},
};
export const attributeIconPath = (
state: HassEntity | undefined,
attribute: string,
attributeValue?: string
) => {
if (!state) {
return undefined;
}
const domain = computeDomain(state.entity_id);
if (iconGenerators[domain]?.[attribute]) {
return iconGenerators[domain]?.[attribute](
attributeValue || state.attributes[attribute]
);
}
return undefined;
};

View File

@ -6,7 +6,6 @@ import { attributeIcon } from "../data/icons";
import { HomeAssistant } from "../types";
import "./ha-icon";
import "./ha-svg-icon";
import { attributeIconPath } from "../common/entity/attribute_icon_path";
@customElement("ha-attribute-icon")
export class HaAttributeIcon extends LitElement {
@ -30,7 +29,7 @@ export class HaAttributeIcon extends LitElement {
}
if (!this.hass) {
return this._renderFallback();
return nothing;
}
const icon = attributeIcon(
@ -42,23 +41,11 @@ export class HaAttributeIcon extends LitElement {
if (icn) {
return html`<ha-icon .icon=${icn}></ha-icon>`;
}
return this._renderFallback();
return nothing;
});
return html`${until(icon)}`;
}
private _renderFallback() {
return html`
<ha-svg-icon
.path=${attributeIconPath(
this.stateObj!,
this.attribute!,
this.attributeValue!
)}
></ha-svg-icon>
`;
}
}
declare global {

View File

@ -84,8 +84,6 @@ export class HaIconPicker extends LitElement {
@property() public placeholder?: string;
@property() public fallbackPath?: string;
@property({ attribute: "error-message" }) public errorMessage?: string;
@property({ type: Boolean }) public disabled = false;
@ -120,12 +118,7 @@ export class HaIconPicker extends LitElement {
<ha-icon .icon=${this._value || this.placeholder} slot="icon">
</ha-icon>
`
: this.fallbackPath
? html`<ha-svg-icon
.path=${this.fallbackPath}
slot="icon"
></ha-svg-icon>`
: ""}
: html`<slot name="fallback"></slot>`}
</ha-combo-box>
`;
}

View File

@ -1,13 +1,12 @@
import { html, LitElement } from "lit";
import { html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { until } from "lit/directives/until";
import { fireEvent } from "../../common/dom/fire_event";
import { computeDomain } from "../../common/entity/compute_domain";
import { domainIcon } from "../../common/entity/domain_icon";
import { entityIcon } from "../../data/icons";
import { IconSelector } from "../../data/selector";
import { HomeAssistant } from "../../types";
import "../ha-icon-picker";
import "../ha-state-icon";
@customElement("ha-selector-icon")
export class HaIconSelector extends LitElement {
@ -39,11 +38,6 @@ export class HaIconSelector extends LitElement {
stateObj?.attributes.icon ||
(stateObj && until(entityIcon(this.hass, stateObj)));
const fallbackPath =
!placeholder && stateObj
? domainIcon(computeDomain(iconEntity!), stateObj)
: undefined;
return html`
<ha-icon-picker
.hass=${this.hass}
@ -52,10 +46,19 @@ export class HaIconSelector extends LitElement {
.required=${this.required}
.disabled=${this.disabled}
.helper=${this.helper}
.fallbackPath=${this.selector.icon?.fallbackPath ?? fallbackPath}
.placeholder=${this.selector.icon?.placeholder ?? placeholder}
@value-changed=${this._valueChanged}
></ha-icon-picker>
>
${!placeholder && stateObj
? html`
<ha-state-icon
slot="fallback"
.hass=${this.hass}
.stateObj=${stateObj}
></ha-state-icon>
`
: nothing}
</ha-icon-picker>
`;
}

View File

@ -1,33 +1,12 @@
import {
mdiAccountArrowRight,
mdiArrowAll,
mdiArrowLeftRight,
mdiArrowOscillating,
mdiArrowOscillatingOff,
mdiArrowUpDown,
mdiBed,
mdiCircleMedium,
mdiClockOutline,
mdiFan,
mdiFanAuto,
mdiFanOff,
mdiFire,
mdiHeatWave,
mdiHome,
mdiLeaf,
mdiMotionSensor,
mdiPower,
mdiRocketLaunch,
mdiSnowflake,
mdiSofa,
mdiSpeedometer,
mdiSpeedometerMedium,
mdiSpeedometerSlow,
mdiSunSnowflakeVariant,
mdiTarget,
mdiThermostat,
mdiThermostatAuto,
mdiWaterPercent,
mdiWeatherWindy,
} from "@mdi/js";
import {
HassEntityAttributeBase,
@ -116,16 +95,6 @@ export const CLIMATE_HVAC_ACTION_TO_MODE: Record<HvacAction, HvacMode> = {
off: "off",
};
export const CLIMATE_HVAC_ACTION_ICONS: Record<HvacAction, string> = {
cooling: mdiSnowflake,
drying: mdiWaterPercent,
fan: mdiFan,
heating: mdiFire,
idle: mdiClockOutline,
off: mdiPower,
preheating: mdiHeatWave,
};
export const CLIMATE_HVAC_MODE_ICONS: Record<HvacMode, string> = {
cool: mdiSnowflake,
dry: mdiWaterPercent,
@ -136,81 +105,5 @@ export const CLIMATE_HVAC_MODE_ICONS: Record<HvacMode, string> = {
heat_cool: mdiSunSnowflakeVariant,
};
export const computeHvacModeIcon = (mode: HvacMode) =>
CLIMATE_HVAC_MODE_ICONS[mode];
type ClimateBuiltInPresetMode =
| "eco"
| "away"
| "boost"
| "comfort"
| "home"
| "sleep"
| "activity";
export const CLIMATE_PRESET_MODE_ICONS: Record<
ClimateBuiltInPresetMode,
string
> = {
away: mdiAccountArrowRight,
boost: mdiRocketLaunch,
comfort: mdiSofa,
eco: mdiLeaf,
home: mdiHome,
sleep: mdiBed,
activity: mdiMotionSensor,
};
export const computePresetModeIcon = (mode: string) =>
mode in CLIMATE_PRESET_MODE_ICONS
? CLIMATE_PRESET_MODE_ICONS[mode]
: mdiCircleMedium;
type ClimateBuiltInFanMode =
| "on"
| "off"
| "auto"
| "low"
| "medium"
| "high"
| "middle"
| "focus"
| "diffuse";
export const CLIMATE_FAN_MODE_ICONS: Record<ClimateBuiltInFanMode, string> = {
on: mdiFan,
off: mdiFanOff,
auto: mdiFanAuto,
low: mdiSpeedometerSlow,
medium: mdiSpeedometerMedium,
high: mdiSpeedometer,
middle: mdiSpeedometerMedium,
focus: mdiTarget,
diffuse: mdiWeatherWindy,
};
export const computeFanModeIcon = (mode: string) =>
mode in CLIMATE_FAN_MODE_ICONS
? CLIMATE_FAN_MODE_ICONS[mode]
: mdiCircleMedium;
type ClimateBuiltInSwingMode =
| "off"
| "on"
| "vertical"
| "horizontal"
| "both";
export const CLIMATE_SWING_MODE_ICONS: Record<ClimateBuiltInSwingMode, string> =
{
on: mdiArrowOscillating,
off: mdiArrowOscillatingOff,
vertical: mdiArrowUpDown,
horizontal: mdiArrowLeftRight,
both: mdiArrowAll,
};
export const computeSwingModeIcon = (mode: string) =>
mode in CLIMATE_SWING_MODE_ICONS
? CLIMATE_SWING_MODE_ICONS[mode]
: mdiCircleMedium;
export const climateHvacModeIcon = (mode: string) =>
CLIMATE_HVAC_MODE_ICONS[mode] || mdiThermostat;

View File

@ -1,19 +1,3 @@
import {
mdiAccountArrowRight,
mdiArrowDownBold,
mdiArrowUpBold,
mdiBabyCarriage,
mdiCircleMedium,
mdiClockOutline,
mdiHome,
mdiLeaf,
mdiPower,
mdiPowerSleep,
mdiRefreshAuto,
mdiRocketLaunch,
mdiSofa,
mdiWaterPercent,
} from "@mdi/js";
import {
HassEntityAttributeBase,
HassEntityBase,
@ -44,39 +28,6 @@ export const enum HumidifierEntityDeviceClass {
DEHUMIDIFIER = "dehumidifier",
}
type HumidifierBuiltInMode =
| "normal"
| "eco"
| "away"
| "boost"
| "comfort"
| "home"
| "sleep"
| "auto"
| "baby";
export const HUMIDIFIER_MODE_ICONS: Record<HumidifierBuiltInMode, string> = {
auto: mdiRefreshAuto,
away: mdiAccountArrowRight,
baby: mdiBabyCarriage,
boost: mdiRocketLaunch,
comfort: mdiSofa,
eco: mdiLeaf,
home: mdiHome,
normal: mdiWaterPercent,
sleep: mdiPowerSleep,
};
export const computeHumidiferModeIcon = (mode?: string) =>
HUMIDIFIER_MODE_ICONS[mode as HumidifierBuiltInMode] ?? mdiCircleMedium;
export const HUMIDIFIER_ACTION_ICONS: Record<HumidifierAction, string> = {
drying: mdiArrowDownBold,
humidifying: mdiArrowUpBold,
idle: mdiClockOutline,
off: mdiPower,
};
export const HUMIDIFIER_ACTION_MODE: Record<HumidifierAction, HumidifierState> =
{
drying: "on",

View File

@ -1,6 +1,11 @@
import { HassEntity } from "home-assistant-js-websocket";
import { computeStateDomain } from "../common/entity/compute_state_domain";
import { HomeAssistant } from "../types";
import {
EntityRegistryDisplayEntry,
EntityRegistryEntry,
} from "./entity_registry";
import { computeDomain } from "../common/entity/compute_domain";
const resources: Record<IconCategory, any> = {
entity: {},
@ -15,7 +20,13 @@ interface PlatformIcons {
[domain: string]: {
[translation_key: string]: {
state: Record<string, string>;
state_attributes: Record<string, { state: Record<string, string> }>;
state_attributes: Record<
string,
{
state: Record<string, string>;
default: string;
}
>;
default: string;
};
};
@ -24,7 +35,13 @@ interface PlatformIcons {
interface ComponentIcons {
[device_class: string]: {
state: Record<string, string>;
state_attributes: Record<string, { state: Record<string, string> }>;
state_attributes: Record<
string,
{
state: Record<string, string>;
default: string;
}
>;
default: string;
};
}
@ -76,32 +93,67 @@ export const entityIcon = async (
state: HassEntity,
stateValue?: string
) => {
let icon: string | undefined;
const domain = computeStateDomain(state);
const entity = hass.entities?.[state.entity_id];
const value = stateValue ?? state.state;
const entity = hass.entities?.[state.entity_id] as
| EntityRegistryDisplayEntry
| undefined;
if (entity?.icon) {
return entity.icon;
}
if (entity?.translation_key && entity.platform) {
const platformIcons = await getPlatformIcons(hass, entity.platform);
const domain = computeStateDomain(state);
const deviceClass = state.attributes.device_class;
return getEntityIcon(
hass,
domain,
deviceClass,
stateValue ?? state.state,
entity?.platform,
entity?.translation_key
);
};
export const entryIcon = async (
hass: HomeAssistant,
entry: EntityRegistryEntry | EntityRegistryDisplayEntry
) => {
if (entry.icon) {
return entry.icon;
}
const domain = computeDomain(entry.entity_id);
return getEntityIcon(
hass,
domain,
undefined,
undefined,
entry.platform,
entry.translation_key
);
};
const getEntityIcon = async (
hass: HomeAssistant,
domain: string,
deviceClass?: string,
value?: string,
platform?: string,
translation_key?: string
) => {
let icon: string | undefined;
if (translation_key && platform) {
const platformIcons = await getPlatformIcons(hass, platform);
if (platformIcons) {
icon =
platformIcons[domain]?.[entity.translation_key]?.state?.[value] ||
platformIcons[domain]?.[entity.translation_key]?.default;
const translations = platformIcons[domain]?.[translation_key];
icon = (value && translations?.state?.[value]) || translations?.default;
}
}
if (!icon) {
const entityComponentIcons = await getComponentIcons(hass, domain);
if (entityComponentIcons) {
icon =
entityComponentIcons[state.attributes.device_class || "_"]?.state?.[
value
] ||
entityComponentIcons._?.state?.[value] ||
entityComponentIcons[state.attributes.device_class || "_"]?.default ||
entityComponentIcons._?.default;
const translations =
(deviceClass && entityComponentIcons[deviceClass]) ||
entityComponentIcons._;
icon = (value && translations?.state?.[value]) || translations?.default;
}
}
return icon;
@ -115,24 +167,32 @@ export const attributeIcon = async (
) => {
let icon: string | undefined;
const domain = computeStateDomain(state);
const entity = hass.entities?.[state.entity_id];
const value = attributeValue ?? state.attributes[attribute];
if (entity?.translation_key && entity.platform) {
const platformIcons = await getPlatformIcons(hass, entity.platform);
const deviceClass = state.attributes.device_class;
const entity = hass.entities?.[state.entity_id] as
| EntityRegistryDisplayEntry
| undefined;
const platform = entity?.platform;
const translation_key = entity?.translation_key;
const value =
attributeValue ??
(state.attributes[attribute] as string | number | undefined);
if (translation_key && platform) {
const platformIcons = await getPlatformIcons(hass, platform);
if (platformIcons) {
icon =
platformIcons[domain]?.[entity.translation_key]?.state_attributes?.[
attribute
]?.state?.[value];
const translations =
platformIcons[domain]?.[translation_key]?.state_attributes?.[attribute];
icon = (value && translations?.state?.[value]) || translations?.default;
}
}
if (!icon) {
const entityComponentIcons = await getComponentIcons(hass, domain);
if (entityComponentIcons) {
icon =
entityComponentIcons[state.attributes.device_class || "_"]
.state_attributes?.[attribute]?.state?.[value] ||
entityComponentIcons._.state_attributes?.[attribute]?.state?.[value];
const translations =
(deviceClass &&
entityComponentIcons[deviceClass]?.state_attributes?.[attribute]) ||
entityComponentIcons._?.state_attributes?.[attribute];
icon = (value && translations?.state?.[value]) || translations?.default;
}
}
return icon;

View File

@ -3,7 +3,6 @@ import {
mdiArrowOscillating,
mdiFan,
mdiThermometer,
mdiThermostat,
mdiTuneVariant,
mdiWaterPercent,
} from "@mdi/js";
@ -11,19 +10,20 @@ import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { property, state } from "lit/decorators";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-attribute-icon";
import "../../../components/ha-control-select-menu";
import "../../../components/ha-icon-button-group";
import "../../../components/ha-icon-button-toggle";
import "../../../components/ha-list-item";
import "../../../components/ha-select";
import "../../../components/ha-switch";
import "../../../components/ha-attribute-icon";
import {
ClimateEntity,
ClimateEntityFeature,
climateHvacModeIcon,
compareClimateHvacModes,
} from "../../../data/climate";
import { UNAVAILABLE, isUnavailableState } from "../../../data/entity";
import { UNAVAILABLE } from "../../../data/entity";
import "../../../state-control/climate/ha-state-control-climate-humidity";
import "../../../state-control/climate/ha-state-control-climate-temperature";
import { HomeAssistant } from "../../../types";
@ -161,31 +161,22 @@ class MoreInfoClimate extends LitElement {
@selected=${this._handleOperationModeChanged}
@closed=${stopPropagation}
>
${!isUnavailableState(this.stateObj.state)
? html`<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="hvac_mode"
.attributeValue=${stateObj.state}
></ha-attribute-icon>`
: html`<ha-svg-icon
slot="icon"
.path=${mdiThermostat}
></ha-svg-icon>`}
${html`
<ha-svg-icon
slot="icon"
.path=${climateHvacModeIcon(stateObj.state)}
></ha-svg-icon>
`}
${stateObj.attributes.hvac_modes
.concat()
.sort(compareClimateHvacModes)
.map(
(mode) => html`
<ha-list-item .value=${mode} graphic="icon">
<ha-attribute-icon
<ha-svg-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="hvac_mode"
.attributeValue=${mode}
></ha-attribute-icon>
.path=${climateHvacModeIcon(mode)}
></ha-svg-icon>
${this.hass.formatEntityState(stateObj, mode)}
</ha-list-item>
`
@ -206,13 +197,15 @@ class MoreInfoClimate extends LitElement {
@closed=${stopPropagation}
>
${stateObj.attributes.preset_mode
? html`<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="preset_mode"
.attributeValue=${stateObj.attributes.preset_mode}
></ha-attribute-icon>`
? html`
<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="preset_mode"
.attributeValue=${stateObj.attributes.preset_mode}
></ha-attribute-icon>
`
: html`
<ha-svg-icon
slot="icon"
@ -255,13 +248,15 @@ class MoreInfoClimate extends LitElement {
@closed=${stopPropagation}
>
${stateObj.attributes.fan_mode
? html`<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="fan_mode"
.attributeValue=${stateObj.attributes.fan_mode}
></ha-attribute-icon>`
? html`
<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="fan_mode"
.attributeValue=${stateObj.attributes.fan_mode}
></ha-attribute-icon>
`
: html`
<ha-svg-icon slot="icon" .path=${mdiFan}></ha-svg-icon>
`}
@ -301,13 +296,15 @@ class MoreInfoClimate extends LitElement {
@closed=${stopPropagation}
>
${stateObj.attributes.swing_mode
? html`<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="swing_mode"
.attributeValue=${stateObj.attributes.swing_mode}
></ha-attribute-icon>`
? html`
<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="swing_mode"
.attributeValue=${stateObj.attributes.swing_mode}
></ha-attribute-icon>
`
: html`
<ha-svg-icon
slot="icon"

View File

@ -4,8 +4,6 @@ import {
mdiFan,
mdiFanOff,
mdiPower,
mdiRotateLeft,
mdiRotateRight,
mdiTuneVariant,
} from "@mdi/js";
import { CSSResultGroup, LitElement, PropertyValues, html, nothing } from "lit";
@ -13,8 +11,8 @@ import { customElement, property, state } from "lit/decorators";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { stateActive } from "../../../common/entity/state_active";
import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-control-select-menu";
import "../../../components/ha-attribute-icon";
import "../../../components/ha-control-select-menu";
import "../../../components/ha-list-item";
import "../../../components/ha-outlined-icon-button";
import { UNAVAILABLE } from "../../../data/entity";
@ -237,12 +235,21 @@ class MoreInfoFan extends LitElement {
@selected=${this._handleDirection}
@closed=${stopPropagation}
>
<ha-svg-icon slot="icon" .path=${mdiRotateLeft}></ha-svg-icon>
<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${this.stateObj}
attribute="direction"
.attributeValue=${this.stateObj.attributes.direction}
></ha-attribute-icon>
<ha-list-item value="forward" graphic="icon">
<ha-svg-icon
<ha-attribute-icon
slot="graphic"
.path=${mdiRotateRight}
></ha-svg-icon>
.hass=${this.hass}
.stateObj=${this.stateObj}
attribute="direction"
attributeValue="forward"
></ha-attribute-icon>
${this.hass.formatEntityAttributeValue(
this.stateObj,
"direction",
@ -250,10 +257,13 @@ class MoreInfoFan extends LitElement {
)}
</ha-list-item>
<ha-list-item value="reverse" graphic="icon">
<ha-svg-icon
<ha-attribute-icon
slot="graphic"
.path=${mdiRotateLeft}
></ha-svg-icon>
.hass=${this.hass}
.stateObj=${this.stateObj}
attribute="direction"
attributeValue="reverse"
></ha-attribute-icon>
${this.hass.formatEntityAttributeValue(
this.stateObj,
"direction",

View File

@ -110,17 +110,21 @@ class MoreInfoHumidifier extends LitElement {
@closed=${stopPropagation}
>
${stateObj.attributes.mode
? html`<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="mode"
.attributeValue=${stateObj.attributes.mode}
></ha-attribute-icon>`
: html`<ha-svg-icon
slot="icon"
.path=${mdiTuneVariant}
></ha-svg-icon>`}
? html`
<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="mode"
.attributeValue=${stateObj.attributes.mode}
></ha-attribute-icon>
`
: html`
<ha-svg-icon
slot="icon"
.path=${mdiTuneVariant}
></ha-svg-icon>
`}
${stateObj.attributes.available_modes!.map(
(mode) => html`
<ha-list-item .value=${mode} graphic="icon">

View File

@ -285,19 +285,19 @@ class MoreInfoLight extends LitElement {
.path=${mdiCreation}
></ha-svg-icon>`}
${this.stateObj.attributes.effect_list?.map(
(mode) => html`
<ha-list-item .value=${mode} graphic="icon">
(effect) => html`
<ha-list-item .value=${effect} graphic="icon">
<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${this.stateObj}
attribute="effect"
.attributeValue=${mode}
.attributeValue=${effect}
></ha-attribute-icon>
${this.hass.formatEntityAttributeValue(
this.stateObj!,
"effect",
mode
effect
)}
</ha-list-item>
`

View File

@ -8,9 +8,8 @@ import {
TemplateResult,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeDomain } from "../../../../common/entity/compute_domain";
import { until } from "lit/directives/until";
import { computeStateName } from "../../../../common/entity/compute_state_name";
import { domainIcon } from "../../../../common/entity/domain_icon";
import { stripPrefixFromEntityName } from "../../../../common/entity/strip_prefix_from_entity_name";
import "../../../../components/ha-card";
import "../../../../components/ha-icon";
@ -19,6 +18,7 @@ import {
ExtEntityRegistryEntry,
getExtendedEntityRegistryEntry,
} from "../../../../data/entity_registry";
import { entryIcon } from "../../../../data/icons";
import { showMoreInfoDialog } from "../../../../dialogs/more-info/show-ha-more-info-dialog";
import type { HomeAssistant } from "../../../../types";
import type { HuiErrorCard } from "../../../lovelace/cards/hui-error-card";
@ -198,6 +198,8 @@ export class HaDeviceEntitiesCard extends LitElement {
entry.name ||
(entry as ExtEntityRegistryEntry).original_name;
const icon = until(entryIcon(this.hass, entry));
return html`
<ha-list-item
graphic="icon"
@ -205,10 +207,7 @@ export class HaDeviceEntitiesCard extends LitElement {
.entry=${entry}
@click=${this._openEditEntry}
>
<ha-svg-icon
slot="graphic"
.path=${domainIcon(computeDomain(entry.entity_id))}
></ha-svg-icon>
<ha-icon slot="graphic" .icon=${icon}></ha-icon>
<div class="name">
${name
? stripPrefixFromEntityName(name, this.deviceName.toLowerCase()) ||

View File

@ -18,7 +18,6 @@ import { fireEvent } from "../../../common/dom/fire_event";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { computeDomain } from "../../../common/entity/compute_domain";
import { computeObjectId } from "../../../common/entity/compute_object_id";
import { domainIcon } from "../../../common/entity/domain_icon";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { formatNumber } from "../../../common/number/format_number";
import { stringCompare } from "../../../common/string/compare";
@ -32,6 +31,7 @@ import "../../../components/ha-area-picker";
import "../../../components/ha-icon";
import "../../../components/ha-icon-button-next";
import "../../../components/ha-icon-picker";
import "../../../components/ha-state-icon";
import "../../../components/ha-list-item";
import "../../../components/ha-radio";
import "../../../components/ha-select";
@ -373,29 +373,30 @@ export class EntityRegistrySettingsEditor extends LitElement {
></ha-textfield>`}
${this.hideIcon
? nothing
: html`<ha-icon-picker
.hass=${this.hass}
.value=${this._icon}
@value-changed=${this._iconChanged}
.label=${this.hass.localize(
"ui.dialogs.entity_registry.editor.icon"
)}
.placeholder=${this.entry.original_icon ||
stateObj?.attributes.icon ||
(stateObj && until(entityIcon(this.hass, stateObj)))}
.fallbackPath=${!this._icon &&
!stateObj?.attributes.icon &&
stateObj
? domainIcon(computeDomain(stateObj.entity_id), {
...stateObj,
attributes: {
...stateObj.attributes,
device_class: this._deviceClass,
},
})
: undefined}
.disabled=${this.disabled}
></ha-icon-picker>`}
: html`
<ha-icon-picker
.hass=${this.hass}
.value=${this._icon}
@value-changed=${this._iconChanged}
.label=${this.hass.localize(
"ui.dialogs.entity_registry.editor.icon"
)}
.placeholder=${this.entry.original_icon ||
stateObj?.attributes.icon ||
(stateObj && until(entityIcon(this.hass, stateObj)))}
.disabled=${this.disabled}
>
${!this._icon && !stateObj?.attributes.icon && stateObj
? html`
<ha-state-icon
slot="fallback"
.hass=${this.hass}
.stateObj=${stateObj}
></ha-state-icon>
`
: nothing}
</ha-icon-picker>
`}
${domain === "switch"
? html`<ha-select
.label=${this.hass.localize(

View File

@ -6,13 +6,13 @@ import { styleMap } from "lit/directives/style-map";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { computeDomain } from "../../../common/entity/compute_domain";
import { stateColorCss } from "../../../common/entity/state_color";
import "../../../components/ha-attribute-icon";
import "../../../components/ha-control-select";
import type { ControlSelectOption } from "../../../components/ha-control-select";
import "../../../components/ha-control-select-menu";
import type { HaControlSelectMenu } from "../../../components/ha-control-select-menu";
import {
ClimateEntity,
climateHvacModeIcon,
compareClimateHvacModes,
HvacMode,
} from "../../../data/climate";
@ -130,13 +130,12 @@ class HuiClimateHvacModesCardFeature
.map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityState(this.stateObj!, mode),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${this.stateObj}
attribute="hvac_mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
icon: html`
<ha-svg-icon
slot="graphic"
.path=${climateHvacModeIcon(mode)}
></ha-svg-icon>
`,
}));
if (this._config.style === "dropdown") {
@ -154,17 +153,15 @@ class HuiClimateHvacModesCardFeature
@closed=${stopPropagation}
>
${this._currentHvacMode
? html`<ha-attribute-icon
slot="icon"
.hass=${this.hass}
.stateObj=${this.stateObj}
attribute="hvac_mode"
.attributeValue=${this._currentHvacMode}
></ha-attribute-icon>`
: html`<ha-svg-icon
slot="icon"
.path=${mdiThermostat}
></ha-svg-icon>`}
? html`
<ha-svg-icon
slot="icon"
.path=${climateHvacModeIcon(this._currentHvacMode)}
></ha-svg-icon>
`
: html`
<ha-svg-icon slot="icon" .path=${mdiThermostat}></ha-svg-icon>
`}
${options.map(
(option) => html`
<ha-list-item .value=${option.value} graphic="icon">

View File

@ -10,9 +10,9 @@ import {
import { RenderBadgeFunction } from "./tile-badge";
export const renderHumidifierBadge: RenderBadgeFunction = (stateObj, hass) => {
const hvacAction = (stateObj as HumidifierEntity).attributes.action;
const action = (stateObj as HumidifierEntity).attributes.action;
if (!hvacAction || hvacAction === "off") {
if (!action || action === "off") {
return nothing;
}
@ -21,7 +21,7 @@ export const renderHumidifierBadge: RenderBadgeFunction = (stateObj, hass) => {
style=${styleMap({
"--tile-badge-background-color": stateColorCss(
stateObj,
HUMIDIFIER_ACTION_MODE[hvacAction]
HUMIDIFIER_ACTION_MODE[action]
),
})}
>