mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 02:06:42 +00:00
Add support for icon translations (#19406)
* Add support for icon translations * Use slot for icons * Update more-info-climate.ts * Review and fixes * Update entity-registry-settings-editor.ts
This commit is contained in:
parent
b969144f50
commit
081636b3e7
@ -344,6 +344,7 @@ export class DemoEntityState extends LitElement {
|
|||||||
title: "Icon",
|
title: "Icon",
|
||||||
template: (entry) => html`
|
template: (entry) => html`
|
||||||
<state-badge
|
<state-badge
|
||||||
|
.hass=${hass}
|
||||||
.stateObj=${entry.stateObj}
|
.stateObj=${entry.stateObj}
|
||||||
.stateColor=${true}
|
.stateColor=${true}
|
||||||
></state-badge>
|
></state-badge>
|
||||||
|
35
src/common/entity/attribute_icon_path.ts
Normal file
35
src/common/entity/attribute_icon_path.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/** Return an icon representing a attribute. */
|
||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import {
|
||||||
|
computeFanModeIcon,
|
||||||
|
computeHvacModeIcon,
|
||||||
|
computePresetModeIcon,
|
||||||
|
computeSwingModeIcon,
|
||||||
|
} from "../../data/climate";
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
@ -4,7 +4,7 @@ import { DEFAULT_DOMAIN_ICON } from "../const";
|
|||||||
import { computeDomain } from "./compute_domain";
|
import { computeDomain } from "./compute_domain";
|
||||||
import { domainIcon } from "./domain_icon";
|
import { domainIcon } from "./domain_icon";
|
||||||
|
|
||||||
export const stateIconPath = (state?: HassEntity) => {
|
export const stateIconPath = (state: HassEntity | undefined) => {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
return DEFAULT_DOMAIN_ICON;
|
return DEFAULT_DOMAIN_ICON;
|
||||||
}
|
}
|
||||||
|
@ -25,16 +25,6 @@ interface HassEntityWithCachedName extends HassEntity, ScorableTextItem {
|
|||||||
|
|
||||||
export type HaEntityPickerEntityFilterFunc = (entity: HassEntity) => boolean;
|
export type HaEntityPickerEntityFilterFunc = (entity: HassEntity) => boolean;
|
||||||
|
|
||||||
// eslint-disable-next-line lit/prefer-static-styles
|
|
||||||
const rowRenderer: ComboBoxLitRenderer<HassEntityWithCachedName> = (item) =>
|
|
||||||
html`<ha-list-item graphic="avatar" .twoline=${!!item.entity_id}>
|
|
||||||
${item.state
|
|
||||||
? html`<state-badge slot="graphic" .stateObj=${item}></state-badge>`
|
|
||||||
: ""}
|
|
||||||
<span>${item.friendly_name}</span>
|
|
||||||
<span slot="secondary">${item.entity_id}</span>
|
|
||||||
</ha-list-item>`;
|
|
||||||
|
|
||||||
@customElement("ha-entity-picker")
|
@customElement("ha-entity-picker")
|
||||||
export class HaEntityPicker extends LitElement {
|
export class HaEntityPicker extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -127,6 +117,21 @@ export class HaEntityPicker extends LitElement {
|
|||||||
|
|
||||||
private _states: HassEntityWithCachedName[] = [];
|
private _states: HassEntityWithCachedName[] = [];
|
||||||
|
|
||||||
|
private _rowRenderer: ComboBoxLitRenderer<HassEntityWithCachedName> = (
|
||||||
|
item
|
||||||
|
) =>
|
||||||
|
html`<ha-list-item graphic="avatar" .twoline=${!!item.entity_id}>
|
||||||
|
${item.state
|
||||||
|
? html`<state-badge
|
||||||
|
slot="graphic"
|
||||||
|
.stateObj=${item}
|
||||||
|
.hass=${this.hass}
|
||||||
|
></state-badge>`
|
||||||
|
: ""}
|
||||||
|
<span>${item.friendly_name}</span>
|
||||||
|
<span slot="secondary">${item.entity_id}</span>
|
||||||
|
</ha-list-item>`;
|
||||||
|
|
||||||
private _getStates = memoizeOne(
|
private _getStates = memoizeOne(
|
||||||
(
|
(
|
||||||
_opened: boolean,
|
_opened: boolean,
|
||||||
@ -326,7 +331,7 @@ export class HaEntityPicker extends LitElement {
|
|||||||
.helper=${this.helper}
|
.helper=${this.helper}
|
||||||
.allowCustomValue=${this.allowCustomEntity}
|
.allowCustomValue=${this.allowCustomEntity}
|
||||||
.filteredItems=${this._states}
|
.filteredItems=${this._states}
|
||||||
.renderer=${rowRenderer}
|
.renderer=${this._rowRenderer}
|
||||||
.required=${this.required}
|
.required=${this.required}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@opened-changed=${this._openedChanged}
|
@opened-changed=${this._openedChanged}
|
||||||
|
@ -140,7 +140,8 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
${!image && showIcon
|
${!image && showIcon
|
||||||
? html`<ha-state-icon
|
? html`<ha-state-icon
|
||||||
.icon=${this.icon}
|
.icon=${this.icon}
|
||||||
.state=${entityState}
|
.stateObj=${entityState}
|
||||||
|
.hass=${this.hass}
|
||||||
></ha-state-icon>`
|
></ha-state-icon>`
|
||||||
: ""}
|
: ""}
|
||||||
${value && !image && !showIcon
|
${value && !image && !showIcon
|
||||||
|
@ -105,6 +105,7 @@ export class HaStatisticPicker extends LitElement {
|
|||||||
? html`<state-badge
|
? html`<state-badge
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.stateObj=${item.state}
|
.stateObj=${item.state}
|
||||||
|
.hass=${this.hass}
|
||||||
></state-badge>`
|
></state-badge>`
|
||||||
: ""}
|
: ""}
|
||||||
<span>${item.name}</span>
|
<span>${item.name}</span>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { mdiAlert } from "@mdi/js";
|
import { mdiAlert } from "@mdi/js";
|
||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
html,
|
|
||||||
LitElement,
|
LitElement,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
nothing,
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
@ -14,8 +14,8 @@ import { styleMap } from "lit/directives/style-map";
|
|||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
||||||
import {
|
import {
|
||||||
stateColorCss,
|
|
||||||
stateColorBrightness,
|
stateColorBrightness,
|
||||||
|
stateColorCss,
|
||||||
} from "../../common/entity/state_color";
|
} from "../../common/entity/state_color";
|
||||||
import { iconColorCSS } from "../../common/style/icon_color_css";
|
import { iconColorCSS } from "../../common/style/icon_color_css";
|
||||||
import { cameraUrlWithWidthHeight } from "../../data/camera";
|
import { cameraUrlWithWidthHeight } from "../../data/camera";
|
||||||
@ -90,11 +90,12 @@ export class StateBadge extends LitElement {
|
|||||||
const domain = stateObj ? computeStateDomain(stateObj) : undefined;
|
const domain = stateObj ? computeStateDomain(stateObj) : undefined;
|
||||||
|
|
||||||
return html`<ha-state-icon
|
return html`<ha-state-icon
|
||||||
|
.hass=${this.hass}
|
||||||
style=${styleMap(this._iconStyle)}
|
style=${styleMap(this._iconStyle)}
|
||||||
data-domain=${ifDefined(domain)}
|
data-domain=${ifDefined(domain)}
|
||||||
data-state=${ifDefined(stateObj?.state)}
|
data-state=${ifDefined(stateObj?.state)}
|
||||||
.icon=${this.overrideIcon}
|
.icon=${this.overrideIcon}
|
||||||
.state=${stateObj}
|
.stateObj=${stateObj}
|
||||||
></ha-state-icon>`;
|
></ha-state-icon>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ class StateInfo extends LitElement {
|
|||||||
const name = computeStateName(this.stateObj);
|
const name = computeStateName(this.stateObj);
|
||||||
|
|
||||||
return html`<state-badge
|
return html`<state-badge
|
||||||
|
.hass=${this.hass}
|
||||||
.stateObj=${this.stateObj}
|
.stateObj=${this.stateObj}
|
||||||
.stateColor=${true}
|
.stateColor=${true}
|
||||||
.color=${this.color}
|
.color=${this.color}
|
||||||
|
68
src/components/ha-attribute-icon.ts
Normal file
68
src/components/ha-attribute-icon.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { until } from "lit/directives/until";
|
||||||
|
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 {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||||
|
|
||||||
|
@property() public attribute?: string;
|
||||||
|
|
||||||
|
@property() public attributeValue?: string;
|
||||||
|
|
||||||
|
@property() public icon?: string;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (this.icon) {
|
||||||
|
return html`<ha-icon .icon=${this.icon}></ha-icon>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.stateObj || !this.attribute) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.hass) {
|
||||||
|
return this._renderFallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
const icon = attributeIcon(
|
||||||
|
this.hass,
|
||||||
|
this.stateObj,
|
||||||
|
this.attribute,
|
||||||
|
this.attributeValue
|
||||||
|
).then((icn) => {
|
||||||
|
if (icn) {
|
||||||
|
return html`<ha-icon .icon=${icn}></ha-icon>`;
|
||||||
|
}
|
||||||
|
return this._renderFallback();
|
||||||
|
});
|
||||||
|
|
||||||
|
return html`${until(icon)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderFallback() {
|
||||||
|
return html`
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${attributeIconPath(
|
||||||
|
this.stateObj!,
|
||||||
|
this.attribute!,
|
||||||
|
this.attributeValue!
|
||||||
|
)}
|
||||||
|
></ha-svg-icon>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-attribute-icon": HaAttributeIcon;
|
||||||
|
}
|
||||||
|
}
|
@ -126,9 +126,9 @@ export class HaControlSelectMenu extends SelectBase {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
${icon && "path" in icon
|
${icon && icon.localName === "ha-svg-icon" && "path" in icon
|
||||||
? html`<ha-svg-icon .path=${icon.path}></ha-svg-icon>`
|
? html`<ha-svg-icon .path=${icon.path}></ha-svg-icon>`
|
||||||
: icon && "icon" in icon
|
: icon && icon.localName === "ha-icon" && "icon" in icon
|
||||||
? html`<ha-icon .path=${icon.icon}></ha-icon>`
|
? html`<ha-icon .path=${icon.icon}></ha-icon>`
|
||||||
: html`<slot name="icon"></slot>`}
|
: html`<slot name="icon"></slot>`}
|
||||||
</div>
|
</div>
|
||||||
|
@ -246,7 +246,8 @@ export class HaRelatedItems extends LitElement {
|
|||||||
graphic="icon"
|
graphic="icon"
|
||||||
>
|
>
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
.state=${entity}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${entity}
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
${entity.attributes.friendly_name || entity.entity_id}
|
${entity.attributes.friendly_name || entity.entity_id}
|
||||||
@ -270,7 +271,8 @@ export class HaRelatedItems extends LitElement {
|
|||||||
graphic="icon"
|
graphic="icon"
|
||||||
>
|
>
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
.state=${group}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${group}
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
${group.attributes.friendly_name || group.entity_id}
|
${group.attributes.friendly_name || group.entity_id}
|
||||||
@ -294,7 +296,8 @@ export class HaRelatedItems extends LitElement {
|
|||||||
graphic="icon"
|
graphic="icon"
|
||||||
>
|
>
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
.state=${scene}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${scene}
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
${scene.attributes.friendly_name || scene.entity_id}
|
${scene.attributes.friendly_name || scene.entity_id}
|
||||||
@ -349,7 +352,8 @@ export class HaRelatedItems extends LitElement {
|
|||||||
graphic="icon"
|
graphic="icon"
|
||||||
>
|
>
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
.state=${automation}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${automation}
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
${automation.attributes.friendly_name ||
|
${automation.attributes.friendly_name ||
|
||||||
@ -403,7 +407,8 @@ export class HaRelatedItems extends LitElement {
|
|||||||
graphic="icon"
|
graphic="icon"
|
||||||
>
|
>
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
.state=${script}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${script}
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
${script.attributes.friendly_name || script.entity_id}
|
${script.attributes.friendly_name || script.entity_id}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { until } from "lit/directives/until";
|
||||||
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 { domainIcon } from "../../common/entity/domain_icon";
|
||||||
|
import { entityIcon } from "../../data/icons";
|
||||||
import { IconSelector } from "../../data/selector";
|
import { IconSelector } from "../../data/selector";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "../ha-icon-picker";
|
import "../ha-icon-picker";
|
||||||
@ -33,7 +35,10 @@ export class HaIconSelector extends LitElement {
|
|||||||
const stateObj = iconEntity ? this.hass.states[iconEntity] : undefined;
|
const stateObj = iconEntity ? this.hass.states[iconEntity] : undefined;
|
||||||
|
|
||||||
const placeholder =
|
const placeholder =
|
||||||
this.selector.icon?.placeholder || stateObj?.attributes.icon;
|
this.selector.icon?.placeholder ||
|
||||||
|
stateObj?.attributes.icon ||
|
||||||
|
(stateObj && until(entityIcon(this.hass, stateObj)));
|
||||||
|
|
||||||
const fallbackPath =
|
const fallbackPath =
|
||||||
!placeholder && stateObj
|
!placeholder && stateObj
|
||||||
? domainIcon(computeDomain(iconEntity!), stateObj)
|
? domainIcon(computeDomain(iconEntity!), stateObj)
|
||||||
|
@ -1,25 +1,49 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { until } from "lit/directives/until";
|
||||||
import { stateIconPath } from "../common/entity/state_icon_path";
|
import { stateIconPath } from "../common/entity/state_icon_path";
|
||||||
|
import { entityIcon } from "../data/icons";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
import "./ha-icon";
|
import "./ha-icon";
|
||||||
import "./ha-svg-icon";
|
import "./ha-svg-icon";
|
||||||
|
|
||||||
@customElement("ha-state-icon")
|
@customElement("ha-state-icon")
|
||||||
export class HaStateIcon extends LitElement {
|
export class HaStateIcon extends LitElement {
|
||||||
@property({ attribute: false }) public state?: HassEntity;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||||
|
|
||||||
@property() public icon?: string;
|
@property() public icon?: string;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render() {
|
||||||
if (this.icon || this.state?.attributes.icon) {
|
if (this.icon || this.stateObj?.attributes.icon) {
|
||||||
return html`<ha-icon
|
return html`<ha-icon
|
||||||
.icon=${this.icon || this.state?.attributes.icon}
|
.icon=${this.icon || this.stateObj?.attributes.icon}
|
||||||
></ha-icon>`;
|
></ha-icon>`;
|
||||||
}
|
}
|
||||||
return html`<ha-svg-icon .path=${stateIconPath(this.state)}></ha-svg-icon>`;
|
if (!this.stateObj) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
if (!this.hass) {
|
||||||
|
return this._renderFallback();
|
||||||
|
}
|
||||||
|
const icon = entityIcon(this.hass, this.stateObj).then((icn) => {
|
||||||
|
if (icn) {
|
||||||
|
return html`<ha-icon .icon=${icn}></ha-icon>`;
|
||||||
|
}
|
||||||
|
return this._renderFallback();
|
||||||
|
});
|
||||||
|
return html`${until(icon)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderFallback() {
|
||||||
|
return html`<ha-svg-icon
|
||||||
|
.path=${stateIconPath(this.stateObj)}
|
||||||
|
></ha-svg-icon>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-state-icon": HaStateIcon;
|
"ha-state-icon": HaStateIcon;
|
||||||
|
@ -224,7 +224,8 @@ export class HaTargetPicker extends LitElement {
|
|||||||
${entityState
|
${entityState
|
||||||
? html`<ha-state-icon
|
? html`<ha-state-icon
|
||||||
class="mdc-chip__icon mdc-chip__icon--leading"
|
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||||
.state=${entityState}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${entityState}
|
||||||
></ha-state-icon>`
|
></ha-state-icon>`
|
||||||
: ""}
|
: ""}
|
||||||
<span role="gridcell">
|
<span role="gridcell">
|
||||||
|
@ -3,6 +3,8 @@ import { HassConfig } from "home-assistant-js-websocket";
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { EntityRegistryEntry } from "./entity_registry";
|
import { EntityRegistryEntry } from "./entity_registry";
|
||||||
|
|
||||||
|
export const connectionContext =
|
||||||
|
createContext<HomeAssistant["connection"]>("connection");
|
||||||
export const statesContext = createContext<HomeAssistant["states"]>("states");
|
export const statesContext = createContext<HomeAssistant["states"]>("states");
|
||||||
export const entitiesContext =
|
export const entitiesContext =
|
||||||
createContext<HomeAssistant["entities"]>("entities");
|
createContext<HomeAssistant["entities"]>("entities");
|
||||||
|
133
src/data/icons.ts
Normal file
133
src/data/icons.ts
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
const resources: Record<IconCategory, any> = {
|
||||||
|
entity: {},
|
||||||
|
entity_component: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IconResources {
|
||||||
|
resources: Record<string, string | Record<string, string>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PlatformIcons {
|
||||||
|
[domain: string]: {
|
||||||
|
[translation_key: string]: {
|
||||||
|
state: Record<string, string>;
|
||||||
|
state_attributes: Record<string, { state: Record<string, string> }>;
|
||||||
|
default: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ComponentIcons {
|
||||||
|
[device_class: string]: {
|
||||||
|
state: Record<string, string>;
|
||||||
|
state_attributes: Record<string, { state: Record<string, string> }>;
|
||||||
|
default: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IconCategory = "entity" | "entity_component";
|
||||||
|
|
||||||
|
export const getHassIcons = async (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
category: IconCategory,
|
||||||
|
integration?: string
|
||||||
|
): Promise<IconResources> =>
|
||||||
|
hass.callWS<{ resources: Record<string, string> }>({
|
||||||
|
type: "frontend/get_icons",
|
||||||
|
category,
|
||||||
|
integration,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getPlatformIcons = async (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
integration: string,
|
||||||
|
force = false
|
||||||
|
): Promise<PlatformIcons> => {
|
||||||
|
if (!force && integration && integration in resources.entity) {
|
||||||
|
return resources.entity[integration];
|
||||||
|
}
|
||||||
|
const result = getHassIcons(hass, "entity", integration);
|
||||||
|
resources.entity[integration] = result.then(
|
||||||
|
(res) => res?.resources[integration]
|
||||||
|
);
|
||||||
|
return resources.entity[integration];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getComponentIcons = async (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
domain: string,
|
||||||
|
force = false
|
||||||
|
): Promise<ComponentIcons> => {
|
||||||
|
if (!force && resources.entity_component) {
|
||||||
|
return resources.entity_component.then((res) => res[domain]);
|
||||||
|
}
|
||||||
|
resources.entity_component = getHassIcons(hass, "entity_component").then(
|
||||||
|
(result) => result.resources
|
||||||
|
);
|
||||||
|
return resources.entity_component.then((res) => res[domain]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const entityIcon = async (hass: HomeAssistant, state: HassEntity) => {
|
||||||
|
let icon: string | undefined;
|
||||||
|
const domain = computeStateDomain(state);
|
||||||
|
const entity = hass.entities?.[state.entity_id];
|
||||||
|
if (entity?.translation_key && entity.platform) {
|
||||||
|
const platformIcons = await getPlatformIcons(hass, entity.platform);
|
||||||
|
if (platformIcons) {
|
||||||
|
icon =
|
||||||
|
platformIcons[domain]?.[entity.translation_key]?.state?.[state.state] ||
|
||||||
|
platformIcons[domain]?.[entity.translation_key]?.default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!icon) {
|
||||||
|
const entityComponentIcons = await getComponentIcons(hass, domain);
|
||||||
|
if (entityComponentIcons) {
|
||||||
|
icon =
|
||||||
|
entityComponentIcons[state.attributes.device_class || "_"]?.state?.[
|
||||||
|
state.state
|
||||||
|
] ||
|
||||||
|
entityComponentIcons._?.state?.[state.state] ||
|
||||||
|
entityComponentIcons[state.attributes.device_class || "_"]?.default ||
|
||||||
|
entityComponentIcons._?.default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return icon;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const attributeIcon = async (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
state: HassEntity,
|
||||||
|
attribute: string,
|
||||||
|
attributeValue?: string
|
||||||
|
) => {
|
||||||
|
let icon: string | undefined;
|
||||||
|
const domain = computeStateDomain(state);
|
||||||
|
const entity = hass.entities?.[state.entity_id];
|
||||||
|
if (entity?.translation_key && entity.platform) {
|
||||||
|
const platformIcons = await getPlatformIcons(hass, entity.platform);
|
||||||
|
if (platformIcons) {
|
||||||
|
icon =
|
||||||
|
platformIcons[domain]?.[entity.translation_key]?.state_attributes?.[
|
||||||
|
attribute
|
||||||
|
]?.state?.[attributeValue || state.attributes[attribute]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!icon) {
|
||||||
|
const entityComponentIcons = await getComponentIcons(hass, domain);
|
||||||
|
if (entityComponentIcons) {
|
||||||
|
icon =
|
||||||
|
entityComponentIcons[state.attributes.device_class || "_"]
|
||||||
|
.state_attributes?.[attribute]?.state?.[
|
||||||
|
attributeValue || state.attributes[attribute]
|
||||||
|
] ||
|
||||||
|
entityComponentIcons._.state_attributes?.[attribute]?.state?.[
|
||||||
|
attributeValue || state.attributes[attribute]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return icon;
|
||||||
|
};
|
@ -17,16 +17,13 @@ import "../../../components/ha-icon-button-toggle";
|
|||||||
import "../../../components/ha-list-item";
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/ha-select";
|
import "../../../components/ha-select";
|
||||||
import "../../../components/ha-switch";
|
import "../../../components/ha-switch";
|
||||||
|
import "../../../components/ha-attribute-icon";
|
||||||
import {
|
import {
|
||||||
ClimateEntity,
|
ClimateEntity,
|
||||||
ClimateEntityFeature,
|
ClimateEntityFeature,
|
||||||
compareClimateHvacModes,
|
compareClimateHvacModes,
|
||||||
computeFanModeIcon,
|
|
||||||
computeHvacModeIcon,
|
|
||||||
computePresetModeIcon,
|
|
||||||
computeSwingModeIcon,
|
|
||||||
} from "../../../data/climate";
|
} from "../../../data/climate";
|
||||||
import { UNAVAILABLE } from "../../../data/entity";
|
import { UNAVAILABLE, isUnavailableState } from "../../../data/entity";
|
||||||
import "../../../state-control/climate/ha-state-control-climate-humidity";
|
import "../../../state-control/climate/ha-state-control-climate-humidity";
|
||||||
import "../../../state-control/climate/ha-state-control-climate-temperature";
|
import "../../../state-control/climate/ha-state-control-climate-temperature";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
@ -164,17 +161,31 @@ class MoreInfoClimate extends LitElement {
|
|||||||
@selected=${this._handleOperationModeChanged}
|
@selected=${this._handleOperationModeChanged}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiThermostat}></ha-svg-icon>
|
${!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>`}
|
||||||
${stateObj.attributes.hvac_modes
|
${stateObj.attributes.hvac_modes
|
||||||
.concat()
|
.concat()
|
||||||
.sort(compareClimateHvacModes)
|
.sort(compareClimateHvacModes)
|
||||||
.map(
|
.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<ha-list-item .value=${mode} graphic="icon">
|
<ha-list-item .value=${mode} graphic="icon">
|
||||||
<ha-svg-icon
|
<ha-attribute-icon
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${computeHvacModeIcon(mode)}
|
.hass=${this.hass}
|
||||||
></ha-svg-icon>
|
.stateObj=${stateObj}
|
||||||
|
attribute="hvac_mode"
|
||||||
|
.attributeValue=${mode}
|
||||||
|
></ha-attribute-icon>
|
||||||
${this.hass.formatEntityState(stateObj, mode)}
|
${this.hass.formatEntityState(stateObj, mode)}
|
||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
@ -194,14 +205,30 @@ class MoreInfoClimate extends LitElement {
|
|||||||
@selected=${this._handlePresetmodeChanged}
|
@selected=${this._handlePresetmodeChanged}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
${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-svg-icon
|
||||||
|
slot="icon"
|
||||||
|
.path=${mdiTuneVariant}
|
||||||
|
></ha-svg-icon>
|
||||||
|
`}
|
||||||
${stateObj.attributes.preset_modes!.map(
|
${stateObj.attributes.preset_modes!.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<ha-list-item .value=${mode} graphic="icon">
|
<ha-list-item .value=${mode} graphic="icon">
|
||||||
<ha-svg-icon
|
<ha-attribute-icon
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${computePresetModeIcon(mode)}
|
.hass=${this.hass}
|
||||||
></ha-svg-icon>
|
.stateObj=${stateObj}
|
||||||
|
attribute="preset_mode"
|
||||||
|
.attributeValue=${mode}
|
||||||
|
></ha-attribute-icon>
|
||||||
${this.hass.formatEntityAttributeValue(
|
${this.hass.formatEntityAttributeValue(
|
||||||
stateObj,
|
stateObj,
|
||||||
"preset_mode",
|
"preset_mode",
|
||||||
@ -227,14 +254,27 @@ class MoreInfoClimate extends LitElement {
|
|||||||
@selected=${this._handleFanModeChanged}
|
@selected=${this._handleFanModeChanged}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiFan}></ha-svg-icon>
|
${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-svg-icon slot="icon" .path=${mdiFan}></ha-svg-icon>
|
||||||
|
`}
|
||||||
${stateObj.attributes.fan_modes!.map(
|
${stateObj.attributes.fan_modes!.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<ha-list-item .value=${mode} graphic="icon">
|
<ha-list-item .value=${mode} graphic="icon">
|
||||||
<ha-svg-icon
|
<ha-attribute-icon
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${computeFanModeIcon(mode)}
|
.hass=${this.hass}
|
||||||
></ha-svg-icon>
|
.stateObj=${stateObj}
|
||||||
|
attribute="fan_mode"
|
||||||
|
.attributeValue=${mode}
|
||||||
|
></ha-attribute-icon>
|
||||||
${this.hass.formatEntityAttributeValue(
|
${this.hass.formatEntityAttributeValue(
|
||||||
stateObj,
|
stateObj,
|
||||||
"fan_mode",
|
"fan_mode",
|
||||||
@ -260,17 +300,30 @@ class MoreInfoClimate extends LitElement {
|
|||||||
@selected=${this._handleSwingmodeChanged}
|
@selected=${this._handleSwingmodeChanged}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
${stateObj.attributes.swing_mode
|
||||||
slot="icon"
|
? html`<ha-attribute-icon
|
||||||
.path=${mdiArrowOscillating}
|
slot="icon"
|
||||||
></ha-svg-icon>
|
.hass=${this.hass}
|
||||||
|
.stateObj=${stateObj}
|
||||||
|
attribute="swing_mode"
|
||||||
|
.attributeValue=${stateObj.attributes.swing_mode}
|
||||||
|
></ha-attribute-icon>`
|
||||||
|
: html`
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="icon"
|
||||||
|
.path=${mdiArrowOscillating}
|
||||||
|
></ha-svg-icon>
|
||||||
|
`}
|
||||||
${stateObj.attributes.swing_modes!.map(
|
${stateObj.attributes.swing_modes!.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<ha-list-item .value=${mode} graphic="icon">
|
<ha-list-item .value=${mode} graphic="icon">
|
||||||
<ha-svg-icon
|
<ha-attribute-icon
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${computeSwingModeIcon(mode)}
|
.hass=${this.hass}
|
||||||
></ha-svg-icon>
|
.stateObj=${stateObj}
|
||||||
|
attribute="swing_mode"
|
||||||
|
.attributeValue=${mode}
|
||||||
|
></ha-attribute-icon>
|
||||||
${this.hass.formatEntityAttributeValue(
|
${this.hass.formatEntityAttributeValue(
|
||||||
stateObj,
|
stateObj,
|
||||||
"swing_mode",
|
"swing_mode",
|
||||||
|
@ -3,16 +3,16 @@ import { customElement, state } from "lit/decorators";
|
|||||||
import { isNavigationClick } from "../common/dom/is-navigation-click";
|
import { isNavigationClick } from "../common/dom/is-navigation-click";
|
||||||
import { navigate } from "../common/navigate";
|
import { navigate } from "../common/navigate";
|
||||||
import { getStorageDefaultPanelUrlPath } from "../data/panel";
|
import { getStorageDefaultPanelUrlPath } from "../data/panel";
|
||||||
|
import { WindowWithPreloads } from "../data/preloads";
|
||||||
import { getRecorderInfo, RecorderInfo } from "../data/recorder";
|
import { getRecorderInfo, RecorderInfo } from "../data/recorder";
|
||||||
import "../resources/custom-card-support";
|
import "../resources/custom-card-support";
|
||||||
import { HassElement } from "../state/hass-element";
|
import { HassElement } from "../state/hass-element";
|
||||||
import QuickBarMixin from "../state/quick-bar-mixin";
|
import QuickBarMixin from "../state/quick-bar-mixin";
|
||||||
import { HomeAssistant, Route } from "../types";
|
import { HomeAssistant, Route } from "../types";
|
||||||
import { WindowWithPreloads } from "../data/preloads";
|
|
||||||
import { storeState } from "../util/ha-pref-storage";
|
import { storeState } from "../util/ha-pref-storage";
|
||||||
import {
|
import {
|
||||||
renderLaunchScreenInfoBox,
|
|
||||||
removeLaunchScreen,
|
removeLaunchScreen,
|
||||||
|
renderLaunchScreenInfoBox,
|
||||||
} from "../util/launch-screen";
|
} from "../util/launch-screen";
|
||||||
import {
|
import {
|
||||||
registerServiceWorker,
|
registerServiceWorker,
|
||||||
|
@ -112,7 +112,11 @@ class PanelCalendar extends LitElement {
|
|||||||
.value=${selCal.entity_id}
|
.value=${selCal.entity_id}
|
||||||
.selected=${!this._deSelectedCalendars.includes(selCal.entity_id)}
|
.selected=${!this._deSelectedCalendars.includes(selCal.entity_id)}
|
||||||
>
|
>
|
||||||
<ha-state-icon slot="graphic" .state=${selCal}></ha-state-icon>
|
<ha-state-icon
|
||||||
|
slot="graphic"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${selCal}
|
||||||
|
></ha-state-icon>
|
||||||
${selCal.name}
|
${selCal.name}
|
||||||
</ha-check-list-item>
|
</ha-check-list-item>
|
||||||
`
|
`
|
||||||
|
@ -123,7 +123,8 @@ class HaAutomationPicker extends LitElement {
|
|||||||
type: "icon",
|
type: "icon",
|
||||||
template: (automation) =>
|
template: (automation) =>
|
||||||
html`<ha-state-icon
|
html`<ha-state-icon
|
||||||
.state=${automation}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${automation}
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
color:
|
color:
|
||||||
automation.state === UNAVAILABLE
|
automation.state === UNAVAILABLE
|
||||||
|
@ -95,6 +95,7 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) {
|
|||||||
slot="graphic"
|
slot="graphic"
|
||||||
.title=${entity.attributes.title ||
|
.title=${entity.attributes.title ||
|
||||||
entity.attributes.friendly_name}
|
entity.attributes.friendly_name}
|
||||||
|
.hass=${this.hass}
|
||||||
.stateObj=${entity}
|
.stateObj=${entity}
|
||||||
class=${ifDefined(
|
class=${ifDefined(
|
||||||
this.narrow && entity.attributes.in_progress
|
this.narrow && entity.attributes.in_progress
|
||||||
|
@ -12,7 +12,6 @@ import { computeDomain } from "../../../../common/entity/compute_domain";
|
|||||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
import { domainIcon } from "../../../../common/entity/domain_icon";
|
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||||
import { stripPrefixFromEntityName } from "../../../../common/entity/strip_prefix_from_entity_name";
|
import { stripPrefixFromEntityName } from "../../../../common/entity/strip_prefix_from_entity_name";
|
||||||
import "../../../../components/entity/state-badge";
|
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-icon";
|
import "../../../../components/ha-icon";
|
||||||
import "../../../../components/ha-list-item";
|
import "../../../../components/ha-list-item";
|
||||||
|
@ -84,7 +84,10 @@ export class EnergyDeviceSettings extends LitElement {
|
|||||||
const entityState = this.hass.states[device.stat_consumption];
|
const entityState = this.hass.states[device.stat_consumption];
|
||||||
return html`
|
return html`
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-state-icon .state=${entityState}></ha-state-icon>
|
<ha-state-icon
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${entityState}
|
||||||
|
></ha-state-icon>
|
||||||
<span class="content"
|
<span class="content"
|
||||||
>${getStatisticLabel(
|
>${getStatisticLabel(
|
||||||
this.hass,
|
this.hass,
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { until } from "lit/directives/until";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
@ -65,6 +66,7 @@ import {
|
|||||||
subscribeEntityRegistry,
|
subscribeEntityRegistry,
|
||||||
updateEntityRegistryEntry,
|
updateEntityRegistryEntry,
|
||||||
} from "../../../data/entity_registry";
|
} from "../../../data/entity_registry";
|
||||||
|
import { entityIcon } from "../../../data/icons";
|
||||||
import { domainToName } from "../../../data/integration";
|
import { domainToName } from "../../../data/integration";
|
||||||
import { getNumberDeviceClassConvertibleUnits } from "../../../data/number";
|
import { getNumberDeviceClassConvertibleUnits } from "../../../data/number";
|
||||||
import {
|
import {
|
||||||
@ -379,7 +381,8 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
|||||||
"ui.dialogs.entity_registry.editor.icon"
|
"ui.dialogs.entity_registry.editor.icon"
|
||||||
)}
|
)}
|
||||||
.placeholder=${this.entry.original_icon ||
|
.placeholder=${this.entry.original_icon ||
|
||||||
stateObj?.attributes.icon}
|
stateObj?.attributes.icon ||
|
||||||
|
(stateObj && until(entityIcon(this.hass, stateObj)))}
|
||||||
.fallbackPath=${!this._icon &&
|
.fallbackPath=${!this._icon &&
|
||||||
!stateObj?.attributes.icon &&
|
!stateObj?.attributes.icon &&
|
||||||
stateObj
|
stateObj
|
||||||
|
@ -212,7 +212,8 @@ export class HaConfigEntities extends LitElement {
|
|||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
title=${ifDefined(entry.entity?.state)}
|
title=${ifDefined(entry.entity?.state)}
|
||||||
slot="item-icon"
|
slot="item-icon"
|
||||||
.state=${entry.entity}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${entry.entity}
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
@ -129,7 +129,10 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
type: "icon",
|
type: "icon",
|
||||||
template: (helper) =>
|
template: (helper) =>
|
||||||
helper.entity
|
helper.entity
|
||||||
? html`<ha-state-icon .state=${helper.entity}></ha-state-icon>`
|
? html`<ha-state-icon
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${helper.entity}
|
||||||
|
></ha-state-icon>`
|
||||||
: html`<ha-svg-icon
|
: html`<ha-svg-icon
|
||||||
.path=${helper.icon}
|
.path=${helper.icon}
|
||||||
style="color: var(--error-color)"
|
style="color: var(--error-color)"
|
||||||
|
@ -91,6 +91,7 @@ class ZHADeviceCard extends SubscribeMixin(LitElement) {
|
|||||||
<state-badge
|
<state-badge
|
||||||
@click=${this._openMoreInfo}
|
@click=${this._openMoreInfo}
|
||||||
.title=${entity.stateName!}
|
.title=${entity.stateName!}
|
||||||
|
.hass=${this.hass}
|
||||||
.stateObj=${this.hass!.states[entity.entity_id]}
|
.stateObj=${this.hass!.states[entity.entity_id]}
|
||||||
slot="item-icon"
|
slot="item-icon"
|
||||||
></state-badge>
|
></state-badge>
|
||||||
|
@ -104,7 +104,10 @@ class HaSceneDashboard extends LitElement {
|
|||||||
),
|
),
|
||||||
type: "icon",
|
type: "icon",
|
||||||
template: (scene) => html`
|
template: (scene) => html`
|
||||||
<ha-state-icon .state=${scene}></ha-state-icon>
|
<ha-state-icon
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${scene}
|
||||||
|
></ha-state-icon>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
|
@ -349,6 +349,7 @@ export class HaSceneEditor extends SubscribeMixin(
|
|||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
>
|
>
|
||||||
<state-badge
|
<state-badge
|
||||||
|
.hass=${this.hass}
|
||||||
.stateObj=${entityStateObj}
|
.stateObj=${entityStateObj}
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
></state-badge>
|
></state-badge>
|
||||||
@ -416,6 +417,7 @@ export class HaSceneEditor extends SubscribeMixin(
|
|||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
>
|
>
|
||||||
<state-badge
|
<state-badge
|
||||||
|
.hass=${this.hass}
|
||||||
.stateObj=${entityStateObj}
|
.stateObj=${entityStateObj}
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
></state-badge>
|
></state-badge>
|
||||||
|
@ -119,7 +119,8 @@ class HaScriptPicker extends LitElement {
|
|||||||
type: "icon",
|
type: "icon",
|
||||||
template: (script) =>
|
template: (script) =>
|
||||||
html`<ha-state-icon
|
html`<ha-state-icon
|
||||||
.state=${script}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${script}
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
color:
|
color:
|
||||||
script.state === UNAVAILABLE ? "var(--error-color)" : "unset",
|
script.state === UNAVAILABLE ? "var(--error-color)" : "unset",
|
||||||
|
@ -153,7 +153,8 @@ class DialogExposeEntity extends LitElement {
|
|||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
title=${ifDefined(entityState?.state)}
|
title=${ifDefined(entityState?.state)}
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.state=${entityState}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${entityState}
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
${computeStateName(entityState)}
|
${computeStateName(entityState)}
|
||||||
<span slot="secondary">${entityState.entity_id}</span>
|
<span slot="secondary">${entityState.entity_id}</span>
|
||||||
|
@ -137,7 +137,8 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||||||
template: (entry) => html`
|
template: (entry) => html`
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
title=${ifDefined(entry.entity?.state)}
|
title=${ifDefined(entry.entity?.state)}
|
||||||
.state=${entry.entity}
|
.stateObj=${entry.entity}
|
||||||
|
.hass=${this.hass}
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
@ -33,10 +33,8 @@ import {
|
|||||||
} from "../../../common/number/format_number";
|
} from "../../../common/number/format_number";
|
||||||
import { subscribeOne } from "../../../common/util/subscribe-one";
|
import { subscribeOne } from "../../../common/util/subscribe-one";
|
||||||
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
|
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
|
||||||
import "../../../components/entity/state-badge";
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-state-icon";
|
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
AreaRegistryEntry,
|
AreaRegistryEntry,
|
||||||
|
@ -214,7 +214,8 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
)}
|
)}
|
||||||
data-state=${ifDefined(stateObj?.state)}
|
data-state=${ifDefined(stateObj?.state)}
|
||||||
.icon=${this._config.icon}
|
.icon=${this._config.icon}
|
||||||
.state=${stateObj}
|
.hass=${this.hass}
|
||||||
|
.stateObj=${stateObj}
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
color: colored ? this._computeColor(stateObj) : undefined,
|
color: colored ? this._computeColor(stateObj) : undefined,
|
||||||
filter: colored ? stateColorBrightness(stateObj) : undefined,
|
filter: colored ? stateColorBrightness(stateObj) : undefined,
|
||||||
|
@ -139,7 +139,8 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
|
|||||||
<div class="icon">
|
<div class="icon">
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
.icon=${this._config.icon}
|
.icon=${this._config.icon}
|
||||||
.state=${stateObj}
|
.stateObj=${stateObj}
|
||||||
|
.hass=${this.hass}
|
||||||
data-domain=${ifDefined(domain)}
|
data-domain=${ifDefined(domain)}
|
||||||
data-state=${stateObj.state}
|
data-state=${stateObj.state}
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
|
@ -148,7 +148,8 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
|
|||||||
>
|
>
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
.icon=${this._config.icon}
|
.icon=${this._config.icon}
|
||||||
.state=${stateObj}
|
.stateObj=${stateObj}
|
||||||
|
.hass=${this.hass}
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -234,7 +234,11 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
|||||||
>
|
>
|
||||||
<div class="top-info">
|
<div class="top-info">
|
||||||
<div class="icon-name">
|
<div class="icon-name">
|
||||||
<ha-state-icon class="icon" .state=${stateObj}></ha-state-icon>
|
<ha-state-icon
|
||||||
|
class="icon"
|
||||||
|
.stateObj=${stateObj}
|
||||||
|
.hass=${this.hass}
|
||||||
|
></ha-state-icon>
|
||||||
<div>
|
<div>
|
||||||
${this._config!.name ||
|
${this._config!.name ||
|
||||||
computeStateName(this.hass!.states[this._config!.entity])}
|
computeStateName(this.hass!.states[this._config!.entity])}
|
||||||
|
@ -181,6 +181,7 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
hui-image {
|
hui-image {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
|
@ -278,7 +278,8 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
|||||||
>
|
>
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
.icon=${entityConf.icon}
|
.icon=${entityConf.icon}
|
||||||
.state=${stateObj}
|
.stateObj=${stateObj}
|
||||||
|
.hass=${this.hass}
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
|
|
||||||
|
@ -144,7 +144,8 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard {
|
|||||||
<div class="icon">
|
<div class="icon">
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
.icon=${this._config.icon}
|
.icon=${this._config.icon}
|
||||||
.state=${stateObj}
|
.stateObj=${stateObj}
|
||||||
|
.hass=${this.hass}
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -256,7 +256,8 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
html`
|
html`
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
class="weather-icon"
|
class="weather-icon"
|
||||||
.state=${stateObj}
|
.stateObj=${stateObj}
|
||||||
|
.hass=${this.hass}
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
|
@ -400,12 +400,14 @@ export class HuiImage extends LitElement {
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
transition: filter 0.2s linear;
|
transition: filter 0.2s linear;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
display: block;
|
display: block;
|
||||||
height: auto;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-container {
|
.progress-container {
|
||||||
@ -428,6 +430,12 @@ export class HuiImage extends LitElement {
|
|||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
.fill img {
|
||||||
|
object-fit: fill;
|
||||||
|
}
|
||||||
|
.contain img {
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
.ratio img,
|
.ratio img,
|
||||||
.ratio div {
|
.ratio div {
|
||||||
|
@ -19,7 +19,6 @@ import {
|
|||||||
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||||
import { customType } from "../../../../common/structs/is-custom-type";
|
import { customType } from "../../../../common/structs/is-custom-type";
|
||||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
||||||
import "../../../../components/entity/state-badge";
|
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-formfield";
|
import "../../../../components/ha-formfield";
|
||||||
import "../../../../components/ha-icon";
|
import "../../../../components/ha-icon";
|
||||||
|
@ -59,6 +59,7 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<state-badge
|
<state-badge
|
||||||
|
.hass=${this.hass}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.title=${computeTooltip(this.hass, this._config)}
|
.title=${computeTooltip(this.hass, this._config)}
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
|
@ -10,7 +10,6 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import "../../../components/entity/state-badge";
|
|
||||||
import { isUnavailableState } from "../../../data/entity";
|
import { isUnavailableState } from "../../../data/entity";
|
||||||
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||||
import {
|
import {
|
||||||
@ -145,7 +144,8 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
|
|||||||
html`
|
html`
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
class="weather-icon"
|
class="weather-icon"
|
||||||
.state=${stateObj}
|
.stateObj=${stateObj}
|
||||||
|
.hass=${this.hass}
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,7 +53,11 @@ export class HuiButtonRow extends LitElement implements LovelaceRow {
|
|||||||
this._config.name ?? (stateObj ? computeStateName(stateObj) : "");
|
this._config.name ?? (stateObj ? computeStateName(stateObj) : "");
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-state-icon .icon=${this._config.icon} .state=${stateObj}>
|
<ha-state-icon
|
||||||
|
.icon=${this._config.icon}
|
||||||
|
.stateObj=${stateObj}
|
||||||
|
.hass=${this.hass}
|
||||||
|
>
|
||||||
</ha-state-icon>
|
</ha-state-icon>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div .title=${name}>${name}</div>
|
<div .title=${name}>${name}</div>
|
||||||
|
@ -167,7 +167,11 @@ class PanelTodo extends LitElement {
|
|||||||
.entityId=${list.entity_id}
|
.entityId=${list.entity_id}
|
||||||
.activated=${list.entity_id === this._entityId}
|
.activated=${list.entity_id === this._entityId}
|
||||||
>
|
>
|
||||||
<ha-state-icon .state=${list} slot="graphic"></ha-state-icon
|
<ha-state-icon
|
||||||
|
.stateObj=${list}
|
||||||
|
.hass=${this.hass}
|
||||||
|
slot="graphic"
|
||||||
|
></ha-state-icon
|
||||||
>${list.name}
|
>${list.name}
|
||||||
</ha-list-item> `
|
</ha-list-item> `
|
||||||
);
|
);
|
||||||
|
@ -40,7 +40,7 @@ class StateCardInputSelect extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<state-badge .stateObj=${this.stateObj}></state-badge>
|
<state-badge .hass=${this.hass} .stateObj=${this.stateObj}></state-badge>
|
||||||
<ha-select
|
<ha-select
|
||||||
.label=${computeStateName(this.stateObj)}
|
.label=${computeStateName(this.stateObj)}
|
||||||
.value=${this.stateObj.state}
|
.value=${this.stateObj.state}
|
||||||
|
@ -17,7 +17,7 @@ class StateCardSelect extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<state-badge .stateObj=${this.stateObj}></state-badge>
|
<state-badge .hass=${this.hass} .stateObj=${this.stateObj}></state-badge>
|
||||||
<ha-select
|
<ha-select
|
||||||
.value=${this.stateObj.state}
|
.value=${this.stateObj.state}
|
||||||
.label=${computeStateName(this.stateObj)}
|
.label=${computeStateName(this.stateObj)}
|
||||||
|
@ -16,7 +16,7 @@ class StateCardText extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<state-badge .stateObj=${this.stateObj}></state-badge>
|
<state-badge .hass=${this.hass} .stateObj=${this.stateObj}></state-badge>
|
||||||
<ha-textfield
|
<ha-textfield
|
||||||
.label=${computeStateName(this.stateObj)}
|
.label=${computeStateName(this.stateObj)}
|
||||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
|
@ -2,6 +2,7 @@ import { ContextProvider } from "@lit-labs/context";
|
|||||||
import {
|
import {
|
||||||
areasContext,
|
areasContext,
|
||||||
configContext,
|
configContext,
|
||||||
|
connectionContext,
|
||||||
devicesContext,
|
devicesContext,
|
||||||
entitiesContext,
|
entitiesContext,
|
||||||
localeContext,
|
localeContext,
|
||||||
@ -24,6 +25,12 @@ export const contextMixin = <T extends Constructor<HassBaseEl>>(
|
|||||||
string,
|
string,
|
||||||
ContextProvider<any> | undefined
|
ContextProvider<any> | undefined
|
||||||
> = {
|
> = {
|
||||||
|
connection: new ContextProvider(this, {
|
||||||
|
context: connectionContext,
|
||||||
|
initialValue: this.hass
|
||||||
|
? this.hass.connection
|
||||||
|
: this._pendingHass.connection,
|
||||||
|
}),
|
||||||
states: new ContextProvider(this, {
|
states: new ContextProvider(this, {
|
||||||
context: statesContext,
|
context: statesContext,
|
||||||
initialValue: this.hass ? this.hass.states : this._pendingHass.states,
|
initialValue: this.hass ? this.hass.states : this._pendingHass.states,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user