Fix localize key type errors for states (#14691)

* Replace unavailable state checks with type predicate

* Remove localize exceptions related to state

* Use literal types for climate attributes

* Add fan action to climate tile badge

* Use literal types for truncated states in badges

* Use literal type for humidifier state

* Replace unavailable state checks in calendar and tile card

* Avoid string split for truncated key
This commit is contained in:
Steve Repsher 2022-12-14 13:39:10 -05:00 committed by GitHub
parent b4d6fc3c20
commit e8e4733fc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 153 additions and 112 deletions

View File

@ -3,6 +3,7 @@ import { HvacAction } from "../../../data/climate";
export const CLIMATE_HVAC_ACTION_COLORS: Record<HvacAction, string> = { export const CLIMATE_HVAC_ACTION_COLORS: Record<HvacAction, string> = {
cooling: "var(--rgb-state-climate-cool-color)", cooling: "var(--rgb-state-climate-cool-color)",
drying: "var(--rgb-state-climate-dry-color)", drying: "var(--rgb-state-climate-dry-color)",
fan: "var(--rgb-state-climate-fan-only-color)",
heating: "var(--rgb-state-climate-heat-color)", heating: "var(--rgb-state-climate-heat-color)",
idle: "var(--rgb-state-climate-idle-color)", idle: "var(--rgb-state-climate-idle-color)",
off: "var(--rgb-state-climate-off-color)", off: "var(--rgb-state-climate-off-color)",

View File

@ -2,7 +2,7 @@ import { HassEntity } from "home-assistant-js-websocket";
import { computeStateDomain } from "./compute_state_domain"; import { computeStateDomain } from "./compute_state_domain";
import { UNAVAILABLE_STATES } from "../../data/entity"; import { UNAVAILABLE_STATES } from "../../data/entity";
const FIXED_DOMAIN_STATES = { export const FIXED_DOMAIN_STATES = {
alarm_control_panel: [ alarm_control_panel: [
"armed_away", "armed_away",
"armed_custom_bypass", "armed_custom_bypass",
@ -57,7 +57,7 @@ const FIXED_DOMAIN_STATES = {
"windy-variant", "windy-variant",
"windy", "windy",
], ],
}; } as const;
const FIXED_DOMAIN_ATTRIBUTE_STATES = { const FIXED_DOMAIN_ATTRIBUTE_STATES = {
alarm_control_panel: { alarm_control_panel: {

View File

@ -1,5 +1,5 @@
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
import { OFF, UNAVAILABLE, UNAVAILABLE_STATES } from "../../data/entity"; import { isUnavailableState, OFF, UNAVAILABLE } from "../../data/entity";
import { computeDomain } from "./compute_domain"; import { computeDomain } from "./compute_domain";
export function stateActive(stateObj: HassEntity, state?: string): boolean { export function stateActive(stateObj: HassEntity, state?: string): boolean {
@ -10,7 +10,7 @@ export function stateActive(stateObj: HassEntity, state?: string): boolean {
return compareState !== UNAVAILABLE; return compareState !== UNAVAILABLE;
} }
if (UNAVAILABLE_STATES.includes(compareState)) { if (isUnavailableState(compareState)) {
return false; return false;
} }

View File

@ -12,9 +12,6 @@ import { getLocalLanguage } from "../../util/common-translation";
export type LocalizeKeys = export type LocalizeKeys =
| FlattenObjectKeys<Omit<TranslationDict, "supervisor">> | FlattenObjectKeys<Omit<TranslationDict, "supervisor">>
| `panel.${string}` | `panel.${string}`
| `state.${string}`
| `state_attributes.${string}`
| `state_badge.${string}`
| `ui.card.alarm_control_panel.${string}` | `ui.card.alarm_control_panel.${string}`
| `ui.card.weather.attributes.${string}` | `ui.card.weather.attributes.${string}`
| `ui.card.weather.cardinal_direction.${string}` | `ui.card.weather.cardinal_direction.${string}`

View File

@ -12,7 +12,7 @@ import { property, state } from "lit/decorators";
import { STATES_OFF } from "../../common/const"; import { STATES_OFF } from "../../common/const";
import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name"; import { computeStateName } from "../../common/entity/compute_state_name";
import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../data/entity"; import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { forwardHaptic } from "../../data/haptics"; import { forwardHaptic } from "../../data/haptics";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import "../ha-formfield"; import "../ha-formfield";
@ -22,7 +22,7 @@ import "../ha-switch";
const isOn = (stateObj?: HassEntity) => const isOn = (stateObj?: HassEntity) =>
stateObj !== undefined && stateObj !== undefined &&
!STATES_OFF.includes(stateObj.state) && !STATES_OFF.includes(stateObj.state) &&
!UNAVAILABLE_STATES.includes(stateObj.state); !isUnavailableState(stateObj.state);
export class HaEntityToggle extends LitElement { export class HaEntityToggle extends LitElement {
// hass is not a property so that we only re-render on stateObj changes // hass is not a property so that we only re-render on stateObj changes

View File

@ -10,21 +10,45 @@ import {
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { arrayLiteralIncludes } from "../../common/array/literal-includes";
import secondsToDuration from "../../common/datetime/seconds_to_duration"; import secondsToDuration from "../../common/datetime/seconds_to_duration";
import { computeStateDisplay } from "../../common/entity/compute_state_display"; import { computeStateDisplay } from "../../common/entity/compute_state_display";
import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name"; import { computeStateName } from "../../common/entity/compute_state_name";
import { FIXED_DOMAIN_STATES } from "../../common/entity/get_states";
import { import {
formatNumber, formatNumber,
getNumberFormatOptions, getNumberFormatOptions,
isNumericState, isNumericState,
} from "../../common/number/format_number"; } from "../../common/number/format_number";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity"; import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { timerTimeRemaining } from "../../data/timer"; import { timerTimeRemaining } from "../../data/timer";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import "../ha-label-badge"; import "../ha-label-badge";
import "../ha-state-icon"; import "../ha-state-icon";
// Define the domains whose states have special truncated strings
const TRUNCATED_DOMAINS = [
"alarm_control_panel",
"device_tracker",
"person",
] as const satisfies ReadonlyArray<keyof typeof FIXED_DOMAIN_STATES>;
type TruncatedDomain = typeof TRUNCATED_DOMAINS[number];
type TruncatedKey = {
[T in TruncatedDomain]: `${T}.${typeof FIXED_DOMAIN_STATES[T][number]}`;
}[TruncatedDomain];
const getTruncatedKey = (domainKey: string, stateKey: string) => {
if (
arrayLiteralIncludes(TRUNCATED_DOMAINS)(domainKey) &&
arrayLiteralIncludes(FIXED_DOMAIN_STATES[domainKey])(stateKey)
) {
return `${domainKey}.${stateKey}` as TruncatedKey;
}
return null;
};
@customElement("ha-state-label-badge") @customElement("ha-state-label-badge")
export class HaStateLabelBadge extends LitElement { export class HaStateLabelBadge extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant; @property({ attribute: false }) public hass?: HomeAssistant;
@ -186,19 +210,18 @@ export class HaStateLabelBadge extends LitElement {
} }
} }
private _computeLabel(domain, entityState, _timerTimeRemaining) { private _computeLabel(
if ( domain: string,
entityState.state === UNAVAILABLE || entityState: HassEntity,
["device_tracker", "alarm_control_panel", "person"].includes(domain) _timerTimeRemaining = 0
) { ) {
// Localize the state with a special state_badge namespace, which has variations of // For unavailable states or certain domains, use a special translation that is truncated to fit within the badge label
// the state translations that are truncated to fit within the badge label. Translations if (isUnavailableState(entityState.state)) {
// are only added for device_tracker, alarm_control_panel and person. return this.hass!.localize(`state_badge.default.${entityState.state}`);
return ( }
this.hass!.localize(`state_badge.${domain}.${entityState.state}`) || const domainStateKey = getTruncatedKey(domain, entityState.state);
this.hass!.localize(`state_badge.default.${entityState.state}`) || if (domainStateKey) {
entityState.state return this.hass!.localize(`state_badge.${domainStateKey}`);
);
} }
if (domain === "timer") { if (domain === "timer") {
return secondsToDuration(_timerTimeRemaining); return secondsToDuration(_timerTimeRemaining);

View File

@ -1,22 +1,21 @@
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { formatNumber } from "../common/number/format_number"; import { formatNumber } from "../common/number/format_number";
import { CLIMATE_PRESET_NONE } from "../data/climate"; import { ClimateEntity, CLIMATE_PRESET_NONE } from "../data/climate";
import { UNAVAILABLE_STATES } from "../data/entity"; import { isUnavailableState } from "../data/entity";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
@customElement("ha-climate-state") @customElement("ha-climate-state")
class HaClimateState extends LitElement { class HaClimateState extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public stateObj!: HassEntity; @property({ attribute: false }) public stateObj!: ClimateEntity;
protected render(): TemplateResult { protected render(): TemplateResult {
const currentStatus = this._computeCurrentStatus(); const currentStatus = this._computeCurrentStatus();
return html`<div class="target"> return html`<div class="target">
${!UNAVAILABLE_STATES.includes(this.stateObj.state) ${!isUnavailableState(this.stateObj.state)
? html`<span class="state-label"> ? html`<span class="state-label">
${this._localizeState()} ${this._localizeState()}
${this.stateObj.attributes.preset_mode && ${this.stateObj.attributes.preset_mode &&
@ -31,7 +30,7 @@ class HaClimateState extends LitElement {
: this._localizeState()} : this._localizeState()}
</div> </div>
${currentStatus && !UNAVAILABLE_STATES.includes(this.stateObj.state) ${currentStatus && !isUnavailableState(this.stateObj.state)
? html`<div class="current"> ? html`<div class="current">
${this.hass.localize("ui.card.climate.currently")}: ${this.hass.localize("ui.card.climate.currently")}:
<div class="unit">${currentStatus}</div> <div class="unit">${currentStatus}</div>
@ -109,7 +108,7 @@ class HaClimateState extends LitElement {
} }
private _localizeState(): string { private _localizeState(): string {
if (UNAVAILABLE_STATES.includes(this.stateObj.state)) { if (isUnavailableState(this.stateObj.state)) {
return this.hass.localize(`state.default.${this.stateObj.state}`); return this.hass.localize(`state.default.${this.stateObj.state}`);
} }

View File

@ -27,7 +27,7 @@ import { until } from "lit/directives/until";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { computeRTLDirection } from "../../common/util/compute_rtl"; import { computeRTLDirection } from "../../common/util/compute_rtl";
import { debounce } from "../../common/util/debounce"; import { debounce } from "../../common/util/debounce";
import { UNAVAILABLE_STATES } from "../../data/entity"; import { isUnavailableState } from "../../data/entity";
import type { MediaPlayerItem } from "../../data/media-player"; import type { MediaPlayerItem } from "../../data/media-player";
import { import {
browseMediaPlayer, browseMediaPlayer,
@ -247,7 +247,7 @@ export class HaMediaPlayerBrowse extends LitElement {
}); });
} else if ( } else if (
err.code === "entity_not_found" && err.code === "entity_not_found" &&
UNAVAILABLE_STATES.includes(this.hass.states[this.entityId]?.state) isUnavailableState(this.hass.states[this.entityId]?.state)
) { ) {
this._setError({ this._setError({
message: this.hass.localize( message: this.hass.localize(

View File

@ -2,7 +2,7 @@ import { getColorByIndex } from "../common/color/colors";
import { computeDomain } from "../common/entity/compute_domain"; import { computeDomain } from "../common/entity/compute_domain";
import { computeStateName } from "../common/entity/compute_state_name"; import { computeStateName } from "../common/entity/compute_state_name";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { UNAVAILABLE_STATES } from "./entity"; import { isUnavailableState } from "./entity";
export interface Calendar { export interface Calendar {
entity_id: string; entity_id: string;
@ -138,7 +138,7 @@ export const getCalendars = (hass: HomeAssistant): Calendar[] =>
.filter( .filter(
(eid) => (eid) =>
computeDomain(eid) === "calendar" && computeDomain(eid) === "calendar" &&
!UNAVAILABLE_STATES.includes(hass.states[eid].state) !isUnavailableState(hass.states[eid].state)
) )
.sort() .sort()
.map((eid, idx) => ({ .map((eid, idx) => ({

View File

@ -2,6 +2,7 @@ import {
HassEntityAttributeBase, HassEntityAttributeBase,
HassEntityBase, HassEntityBase,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { TranslationDict } from "../types";
export type HvacMode = export type HvacMode =
| "off" | "off"
@ -14,7 +15,12 @@ export type HvacMode =
export const CLIMATE_PRESET_NONE = "none"; export const CLIMATE_PRESET_NONE = "none";
export type HvacAction = "off" | "heating" | "cooling" | "drying" | "idle"; type ClimateAttributes = TranslationDict["state_attributes"]["climate"];
export type HvacAction = keyof ClimateAttributes["hvac_action"];
export type FanMode = keyof ClimateAttributes["fan_mode"];
export type PresetMode =
| keyof ClimateAttributes["preset_mode"]
| typeof CLIMATE_PRESET_NONE;
export type ClimateEntity = HassEntityBase & { export type ClimateEntity = HassEntityBase & {
attributes: HassEntityAttributeBase & { attributes: HassEntityAttributeBase & {
@ -34,10 +40,10 @@ export type ClimateEntity = HassEntityBase & {
target_humidity_high?: number; target_humidity_high?: number;
min_humidity?: number; min_humidity?: number;
max_humidity?: number; max_humidity?: number;
fan_mode?: string; fan_mode?: FanMode;
fan_modes?: string[]; fan_modes?: FanMode[];
preset_mode?: string; preset_mode?: PresetMode;
preset_modes?: string[]; preset_modes?: PresetMode[];
swing_mode?: string; swing_mode?: string;
swing_modes?: string[]; swing_modes?: string[];
aux_heat?: "on" | "off"; aux_heat?: "on" | "off";

View File

@ -1,7 +1,12 @@
import { arrayLiteralIncludes } from "../common/array/literal-includes";
export const UNAVAILABLE = "unavailable"; export const UNAVAILABLE = "unavailable";
export const UNKNOWN = "unknown"; export const UNKNOWN = "unknown";
export const ON = "on"; export const ON = "on";
export const OFF = "off"; export const OFF = "off";
export const UNAVAILABLE_STATES = [UNAVAILABLE, UNKNOWN]; export const UNAVAILABLE_STATES = [UNAVAILABLE, UNKNOWN] as const;
export const OFF_STATES = [UNAVAILABLE, UNKNOWN, OFF]; export const OFF_STATES = [UNAVAILABLE, UNKNOWN, OFF] as const;
export const isUnavailableState = arrayLiteralIncludes(UNAVAILABLE_STATES);
export const isOffState = arrayLiteralIncludes(OFF_STATES);

View File

@ -2,14 +2,24 @@ import {
HassEntityAttributeBase, HassEntityAttributeBase,
HassEntityBase, HassEntityBase,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { FIXED_DOMAIN_STATES } from "../common/entity/get_states";
import { TranslationDict } from "../types";
import { UNAVAILABLE_STATES } from "./entity";
type HumidifierState =
| typeof FIXED_DOMAIN_STATES.humidifier[number]
| typeof UNAVAILABLE_STATES[number];
type HumidifierMode =
keyof TranslationDict["state_attributes"]["humidifier"]["mode"];
export type HumidifierEntity = HassEntityBase & { export type HumidifierEntity = HassEntityBase & {
state: HumidifierState;
attributes: HassEntityAttributeBase & { attributes: HassEntityAttributeBase & {
humidity?: number; humidity?: number;
min_humidity?: number; min_humidity?: number;
max_humidity?: number; max_humidity?: number;
mode?: string; mode?: HumidifierMode;
available_modes?: string[]; available_modes?: HumidifierMode[];
}; };
}; };

View File

@ -35,7 +35,7 @@ import type {
import { supportsFeature } from "../common/entity/supports-feature"; import { supportsFeature } from "../common/entity/supports-feature";
import { MediaPlayerItemId } from "../components/media-player/ha-media-player-browse"; import { MediaPlayerItemId } from "../components/media-player/ha-media-player-browse";
import type { HomeAssistant, TranslationDict } from "../types"; import type { HomeAssistant, TranslationDict } from "../types";
import { UNAVAILABLE_STATES } from "./entity"; import { isUnavailableState } from "./entity";
import { isTTSMediaSource } from "./tts"; import { isTTSMediaSource } from "./tts";
interface MediaPlayerEntityAttributes extends HassEntityAttributeBase { interface MediaPlayerEntityAttributes extends HassEntityAttributeBase {
@ -259,7 +259,7 @@ export const computeMediaControls = (
const state = stateObj.state; const state = stateObj.state;
if (UNAVAILABLE_STATES.includes(state)) { if (isUnavailableState(state)) {
return undefined; return undefined;
} }

View File

@ -4,7 +4,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import "../../../components/ha-relative-time"; import "../../../components/ha-relative-time";
import { triggerAutomationActions } from "../../../data/automation"; import { triggerAutomationActions } from "../../../data/automation";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
@customElement("more-info-automation") @customElement("more-info-automation")
@ -32,7 +32,7 @@ class MoreInfoAutomation extends LitElement {
<div class="actions"> <div class="actions">
<mwc-button <mwc-button
@click=${this._runActions} @click=${this._runActions}
.disabled=${UNAVAILABLE_STATES.includes(this.stateObj!.state)} .disabled=${isUnavailableState(this.stateObj!.state)}
> >
${this.hass.localize("ui.card.automation.trigger")} ${this.hass.localize("ui.card.automation.trigger")}
</mwc-button> </mwc-button>

View File

@ -2,7 +2,7 @@ import "@material/mwc-button";
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
@customElement("more-info-counter") @customElement("more-info-counter")
@ -16,7 +16,7 @@ class MoreInfoCounter extends LitElement {
return html``; return html``;
} }
const disabled = UNAVAILABLE_STATES.includes(this.stateObj!.state); const disabled = isUnavailableState(this.stateObj!.state);
return html` return html`
<div class="actions"> <div class="actions">

View File

@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import "../../../components/ha-date-input"; import "../../../components/ha-date-input";
import "../../../components/ha-time-input"; import "../../../components/ha-time-input";
import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; import { isUnavailableState, UNKNOWN } from "../../../data/entity";
import { import {
setInputDateTimeValue, setInputDateTimeValue,
stateToIsoDateString, stateToIsoDateString,
@ -28,7 +28,7 @@ class MoreInfoInputDatetime extends LitElement {
<ha-date-input <ha-date-input
.locale=${this.hass.locale} .locale=${this.hass.locale}
.value=${stateToIsoDateString(this.stateObj)} .value=${stateToIsoDateString(this.stateObj)}
.disabled=${UNAVAILABLE_STATES.includes(this.stateObj.state)} .disabled=${isUnavailableState(this.stateObj.state)}
@value-changed=${this._dateChanged} @value-changed=${this._dateChanged}
> >
</ha-date-input> </ha-date-input>
@ -45,7 +45,7 @@ class MoreInfoInputDatetime extends LitElement {
? this.stateObj.state.split(" ")[1] ? this.stateObj.state.split(" ")[1]
: this.stateObj.state} : this.stateObj.state}
.locale=${this.hass.locale} .locale=${this.hass.locale}
.disabled=${UNAVAILABLE_STATES.includes(this.stateObj.state)} .disabled=${isUnavailableState(this.stateObj.state)}
@value-changed=${this._timeChanged} @value-changed=${this._timeChanged}
@click=${this._stopEventPropagation} @click=${this._stopEventPropagation}
></ha-time-input> ></ha-time-input>

View File

@ -9,7 +9,7 @@ import "../../../components/ha-checkbox";
import "../../../components/ha-circular-progress"; import "../../../components/ha-circular-progress";
import "../../../components/ha-formfield"; import "../../../components/ha-formfield";
import "../../../components/ha-markdown"; import "../../../components/ha-markdown";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { import {
UpdateEntity, UpdateEntity,
updateIsInstalling, updateIsInstalling,
@ -37,7 +37,7 @@ class MoreInfoUpdate extends LitElement {
if ( if (
!this.hass || !this.hass ||
!this.stateObj || !this.stateObj ||
UNAVAILABLE_STATES.includes(this.stateObj.state) isUnavailableState(this.stateObj.state)
) { ) {
return html``; return html``;
} }

View File

@ -45,7 +45,7 @@ import { showToast } from "../../../util/toast";
import { configSections } from "../ha-panel-config"; import { configSections } from "../ha-panel-config";
import { formatShortDateTime } from "../../../common/datetime/format_date_time"; import { formatShortDateTime } from "../../../common/datetime/format_date_time";
import { relativeTime } from "../../../common/datetime/relative_time"; import { relativeTime } from "../../../common/datetime/relative_time";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
@customElement("ha-scene-dashboard") @customElement("ha-scene-dashboard")
class HaSceneDashboard extends LitElement { class HaSceneDashboard extends LitElement {
@ -116,7 +116,7 @@ class HaSceneDashboard extends LitElement {
const now = new Date(); const now = new Date();
const dayDifference = differenceInDays(now, date); const dayDifference = differenceInDays(now, date);
return html` return html`
${last_activated && !UNAVAILABLE_STATES.includes(last_activated) ${last_activated && !isUnavailableState(last_activated)
? dayDifference > 3 ? dayDifference > 3
? formatShortDateTime(date, this.hass.locale) ? formatShortDateTime(date, this.hass.locale)
: relativeTime(date, this.hass.locale) : relativeTime(date, this.hass.locale)

View File

@ -42,7 +42,7 @@ import {
DeviceRegistryEntry, DeviceRegistryEntry,
subscribeDeviceRegistry, subscribeDeviceRegistry,
} from "../../../data/device_registry"; } from "../../../data/device_registry";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { import {
EntityRegistryEntry, EntityRegistryEntry,
subscribeEntityRegistry, subscribeEntityRegistry,
@ -181,8 +181,7 @@ export class HuiAreaCard
: entities : entities
).some( ).some(
(entity) => (entity) =>
!UNAVAILABLE_STATES.includes(entity.state) && !isUnavailableState(entity.state) && !STATES_OFF.includes(entity.state)
!STATES_OFF.includes(entity.state)
); );
} }

View File

@ -27,7 +27,7 @@ import {
import { iconColorCSS } from "../../../common/style/icon_color_css"; import { iconColorCSS } from "../../../common/style/icon_color_css";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-icon"; import "../../../components/ha-icon";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { formatAttributeValue } from "../../../data/entity_attributes"; import { formatAttributeValue } from "../../../data/entity_attributes";
import { LightEntity } from "../../../data/light"; import { LightEntity } from "../../../data/light";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
@ -130,7 +130,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
const domain = computeStateDomain(stateObj); const domain = computeStateDomain(stateObj);
const showUnit = this._config.attribute const showUnit = this._config.attribute
? this._config.attribute in stateObj.attributes ? this._config.attribute in stateObj.attributes
: !UNAVAILABLE_STATES.includes(stateObj.state); : !isUnavailableState(stateObj.state);
const name = this._config.name || computeStateName(stateObj); const name = this._config.name || computeStateName(stateObj);

View File

@ -17,7 +17,7 @@ import "../../../components/entity/state-badge";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-icon"; import "../../../components/ha-icon";
import "../../../components/ha-relative-time"; import "../../../components/ha-relative-time";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { import {
ActionHandlerEvent, ActionHandlerEvent,
CallServiceActionConfig, CallServiceActionConfig,
@ -315,7 +315,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
${computeDomain(entityConf.entity) === "sensor" && ${computeDomain(entityConf.entity) === "sensor" &&
stateObj.attributes.device_class === stateObj.attributes.device_class ===
SENSOR_DEVICE_CLASS_TIMESTAMP && SENSOR_DEVICE_CLASS_TIMESTAMP &&
!UNAVAILABLE_STATES.includes(stateObj.state) !isUnavailableState(stateObj.state)
? html` ? html`
<hui-timestamp-display <hui-timestamp-display
.hass=${this.hass} .hass=${this.hass}

View File

@ -18,7 +18,7 @@ import { computeStateName } from "../../../common/entity/compute_state_name";
import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { HumidifierEntity } from "../../../data/humidifier"; import { HumidifierEntity } from "../../../data/humidifier";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
@ -97,7 +97,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
const rtlDirection = computeRTLDirection(this.hass); const rtlDirection = computeRTLDirection(this.hass);
const slider = UNAVAILABLE_STATES.includes(stateObj.state) const slider = isUnavailableState(stateObj.state)
? html` <round-slider disabled="true"></round-slider> ` ? html` <round-slider disabled="true"></round-slider> `
: html` : html`
<round-slider <round-slider
@ -115,7 +115,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
const setValues = html` const setValues = html`
<svg viewBox="0 0 24 20"> <svg viewBox="0 0 24 20">
<text x="50%" dx="1" y="73%" text-anchor="middle" id="set-values"> <text x="50%" dx="1" y="73%" text-anchor="middle" id="set-values">
${UNAVAILABLE_STATES.includes(stateObj.state) || ${isUnavailableState(stateObj.state) ||
setHumidity === undefined || setHumidity === undefined ||
setHumidity === null setHumidity === null
? "" ? ""
@ -132,8 +132,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
<svg viewBox="0 0 40 10" id="humidity"> <svg viewBox="0 0 40 10" id="humidity">
<text x="50%" y="50%" text-anchor="middle" id="set-mode"> <text x="50%" y="50%" text-anchor="middle" id="set-mode">
${this.hass!.localize(`state.default.${stateObj.state}`)} ${this.hass!.localize(`state.default.${stateObj.state}`)}
${stateObj.attributes.mode && ${stateObj.attributes.mode && !isUnavailableState(stateObj.state)
!UNAVAILABLE_STATES.includes(stateObj.state)
? html` ? html`
- -
${this.hass!.localize( ${this.hass!.localize(
@ -161,7 +160,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
<div id="slider-center"> <div id="slider-center">
<ha-icon-button <ha-icon-button
class="toggle-button" class="toggle-button"
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} .disabled=${isUnavailableState(stateObj.state)}
@click=${this._toggle} @click=${this._toggle}
tabindex="0" tabindex="0"
> >
@ -225,7 +224,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
} }
private _getSetHum(stateObj: HassEntity): undefined | number { private _getSetHum(stateObj: HassEntity): undefined | number {
if (UNAVAILABLE_STATES.includes(stateObj.state)) { if (isUnavailableState(stateObj.state)) {
return undefined; return undefined;
} }

View File

@ -18,7 +18,7 @@ import { computeStateName } from "../../../common/entity/compute_state_name";
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-state-icon";
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState, UNAVAILABLE } from "../../../data/entity";
import { LightEntity, lightSupportsBrightness } from "../../../data/light"; import { LightEntity, lightSupportsBrightness } from "../../../data/light";
import { ActionHandlerEvent } from "../../../data/lovelace"; import { ActionHandlerEvent } from "../../../data/lovelace";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
@ -118,7 +118,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
min="1" min="1"
max="100" max="100"
.value=${brightness} .value=${brightness}
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} .disabled=${isUnavailableState(stateObj.state)}
@value-changing=${this._dragEvent} @value-changing=${this._dragEvent}
@value-changed=${this._setBrightness} @value-changed=${this._setBrightness}
style=${styleMap({ style=${styleMap({
@ -133,7 +133,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
"state-on": stateObj.state === "on", "state-on": stateObj.state === "on",
"state-unavailable": stateObj.state === UNAVAILABLE, "state-unavailable": stateObj.state === UNAVAILABLE,
})}" })}"
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} .disabled=${isUnavailableState(stateObj.state)}
style=${styleMap({ style=${styleMap({
filter: this._computeBrightness(stateObj), filter: this._computeBrightness(stateObj),
color: this._computeColor(stateObj), color: this._computeColor(stateObj),
@ -154,7 +154,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
</div> </div>
<div id="info" .title=${name}> <div id="info" .title=${name}>
${UNAVAILABLE_STATES.includes(stateObj.state) ${isUnavailableState(stateObj.state)
? html` ? html`
<div> <div>
${computeStateDisplay( ${computeStateDisplay(

View File

@ -22,7 +22,7 @@ import "../../../components/ha-card";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import "../../../components/ha-state-icon"; import "../../../components/ha-state-icon";
import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog"; import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { import {
cleanupMediaTitle, cleanupMediaTitle,
computeMediaControls, computeMediaControls,
@ -173,7 +173,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
const isOffState = entityState === "off"; const isOffState = entityState === "off";
const isUnavailable = const isUnavailable =
UNAVAILABLE_STATES.includes(entityState) || isUnavailableState(entityState) ||
(entityState === "off" && !supportsFeature(stateObj, SUPPORT_TURN_ON)); (entityState === "off" && !supportsFeature(stateObj, SUPPORT_TURN_ON));
const hasNoImage = !this._image; const hasNoImage = !this._image;
const controls = computeMediaControls(stateObj, false); const controls = computeMediaControls(stateObj, false);

View File

@ -20,7 +20,7 @@ import "../../../components/tile/ha-tile-image";
import "../../../components/tile/ha-tile-info"; import "../../../components/tile/ha-tile-info";
import { cameraUrlWithWidthHeight } from "../../../data/camera"; import { cameraUrlWithWidthHeight } from "../../../data/camera";
import { CoverEntity } from "../../../data/cover"; import { CoverEntity } from "../../../data/cover";
import { ON, UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState, ON } from "../../../data/entity";
import { FanEntity } from "../../../data/fan"; import { FanEntity } from "../../../data/fan";
import { LightEntity } from "../../../data/light"; import { LightEntity } from "../../../data/light";
import { ActionHandlerEvent } from "../../../data/lovelace"; import { ActionHandlerEvent } from "../../../data/lovelace";
@ -171,7 +171,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
if ( if (
(stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP || (stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP ||
TIMESTAMP_STATE_DOMAINS.includes(domain)) && TIMESTAMP_STATE_DOMAINS.includes(domain)) &&
!UNAVAILABLE_STATES.includes(stateObj.state) !isUnavailableState(stateObj.state)
) { ) {
return html` return html`
<hui-timestamp-display <hui-timestamp-display

View File

@ -1,5 +1,6 @@
import { import {
mdiClockOutline, mdiClockOutline,
mdiFan,
mdiFire, mdiFire,
mdiPower, mdiPower,
mdiSnowflake, mdiSnowflake,
@ -12,6 +13,7 @@ import { ComputeBadgeFunction } from "./tile-badge";
export const CLIMATE_HVAC_ACTION_ICONS: Record<HvacAction, string> = { export const CLIMATE_HVAC_ACTION_ICONS: Record<HvacAction, string> = {
cooling: mdiSnowflake, cooling: mdiSnowflake,
drying: mdiWaterPercent, drying: mdiWaterPercent,
fan: mdiFan,
heating: mdiFire, heating: mdiFire,
idle: mdiClockOutline, idle: mdiClockOutline,
off: mdiPower, off: mdiPower,

View File

@ -17,7 +17,7 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/search-input"; import "../../../../components/search-input";
import "../../../../components/ha-circular-progress"; import "../../../../components/ha-circular-progress";
import { UNAVAILABLE_STATES } from "../../../../data/entity"; import { isUnavailableState } from "../../../../data/entity";
import type { import type {
LovelaceCardConfig, LovelaceCardConfig,
LovelaceConfig, LovelaceConfig,
@ -163,12 +163,12 @@ export class HuiCardPicker extends LitElement {
this._usedEntities = [...usedEntities].filter( this._usedEntities = [...usedEntities].filter(
(eid) => (eid) =>
this.hass!.states[eid] && this.hass!.states[eid] &&
!UNAVAILABLE_STATES.includes(this.hass!.states[eid].state) !isUnavailableState(this.hass!.states[eid].state)
); );
this._unusedEntities = [...unusedEntities].filter( this._unusedEntities = [...unusedEntities].filter(
(eid) => (eid) =>
this.hass!.states[eid] && this.hass!.states[eid] &&
!UNAVAILABLE_STATES.includes(this.hass!.states[eid].state) !isUnavailableState(this.hass!.states[eid].state)
); );
this._loadCards(); this._loadCards();

View File

@ -1,6 +1,7 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import "../../../components/entity/ha-entity-toggle"; import "../../../components/entity/ha-entity-toggle";
import { HumidifierEntity } from "../../../data/humidifier";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row"; import "../components/hui-generic-entity-row";
@ -30,7 +31,7 @@ class HuiHumidifierEntityRow extends LitElement implements LovelaceRow {
return html``; return html``;
} }
const stateObj = this.hass.states[this._config.entity]; const stateObj = this.hass.states[this._config.entity] as HumidifierEntity;
if (!stateObj) { if (!stateObj) {
return html` return html`

View File

@ -8,7 +8,7 @@ import {
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import "../../../components/ha-date-input"; import "../../../components/ha-date-input";
import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; import { isUnavailableState, UNKNOWN } from "../../../data/entity";
import { import {
setInputDateTimeValue, setInputDateTimeValue,
stateToIsoDateString, stateToIsoDateString,
@ -67,7 +67,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
<ha-date-input <ha-date-input
.label=${stateObj.attributes.has_time ? name : undefined} .label=${stateObj.attributes.has_time ? name : undefined}
.locale=${this.hass.locale} .locale=${this.hass.locale}
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} .disabled=${isUnavailableState(stateObj.state)}
.value=${stateToIsoDateString(stateObj)} .value=${stateToIsoDateString(stateObj)}
@value-changed=${this._dateChanged} @value-changed=${this._dateChanged}
> >
@ -83,7 +83,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
? stateObj.state.split(" ")[1] ? stateObj.state.split(" ")[1]
: stateObj.state} : stateObj.state}
.locale=${this.hass.locale} .locale=${this.hass.locale}
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} .disabled=${isUnavailableState(stateObj.state)}
@value-changed=${this._timeChanged} @value-changed=${this._timeChanged}
@click=${this._stopEventPropagation} @click=${this._stopEventPropagation}
></ha-time-input> ></ha-time-input>

View File

@ -12,7 +12,7 @@ import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { debounce } from "../../../common/util/debounce"; import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-slider"; import "../../../components/ha-slider";
import "../../../components/ha-textfield"; import "../../../components/ha-textfield";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { setValue } from "../../../data/input_text"; import { setValue } from "../../../data/input_text";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
@ -85,7 +85,7 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow {
? html` ? html`
<div class="flex"> <div class="flex">
<ha-slider <ha-slider
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} .disabled=${isUnavailableState(stateObj.state)}
.dir=${computeRTLDirection(this.hass)} .dir=${computeRTLDirection(this.hass)}
.step=${Number(stateObj.attributes.step)} .step=${Number(stateObj.attributes.step)}
.min=${Number(stateObj.attributes.min)} .min=${Number(stateObj.attributes.min)}
@ -109,7 +109,7 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow {
: html` : html`
<div class="flex state"> <div class="flex state">
<ha-textfield <ha-textfield
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} .disabled=${isUnavailableState(stateObj.state)}
pattern="[0-9]+([\\.][0-9]+)?" pattern="[0-9]+([\\.][0-9]+)?"
.step=${Number(stateObj.attributes.step)} .step=${Number(stateObj.attributes.step)}
.min=${Number(stateObj.attributes.min)} .min=${Number(stateObj.attributes.min)}

View File

@ -1,6 +1,6 @@
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState, UNAVAILABLE } from "../../../data/entity";
import { setValue } from "../../../data/input_text"; import { setValue } from "../../../data/input_text";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
@ -70,7 +70,7 @@ class HuiInputTextEntityRow extends LitElement implements LovelaceRow {
const newValue = ev.target.value; const newValue = ev.target.value;
// Filter out invalid text states // Filter out invalid text states
if (newValue && UNAVAILABLE_STATES.includes(newValue)) { if (newValue && isUnavailableState(newValue)) {
ev.target.value = stateObj.state; ev.target.value = stateObj.state;
return; return;
} }

View File

@ -8,7 +8,7 @@ import {
TemplateResult, TemplateResult,
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row"; import "../components/hui-generic-entity-row";
@ -51,7 +51,7 @@ class HuiLockEntityRow extends LitElement implements LovelaceRow {
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}> <hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
<mwc-button <mwc-button
@click=${this._callService} @click=${this._callService}
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} .disabled=${isUnavailableState(stateObj.state)}
class="text-content" class="text-content"
> >
${stateObj.state === "locked" ${stateObj.state === "locked"

View File

@ -27,7 +27,7 @@ import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { debounce } from "../../../common/util/debounce"; import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import "../../../components/ha-slider"; import "../../../components/ha-slider";
import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../../data/entity";
import { import {
computeMediaDescription, computeMediaDescription,
ControlButton, ControlButton,
@ -203,7 +203,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
<div class="controls"> <div class="controls">
${supportsFeature(stateObj, SUPPORT_TURN_ON) && ${supportsFeature(stateObj, SUPPORT_TURN_ON) &&
entityState === "off" && entityState === "off" &&
!UNAVAILABLE_STATES.includes(entityState) !isUnavailableState(entityState)
? html` ? html`
<ha-icon-button <ha-icon-button
.path=${mdiPower} .path=${mdiPower}
@ -217,7 +217,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
: ""} : ""}
${supportsFeature(stateObj, SUPPORT_TURN_OFF) && ${supportsFeature(stateObj, SUPPORT_TURN_OFF) &&
entityState !== "off" && entityState !== "off" &&
!UNAVAILABLE_STATES.includes(entityState) !isUnavailableState(entityState)
? html` ? html`
<ha-icon-button <ha-icon-button
.path=${mdiPower} .path=${mdiPower}

View File

@ -8,7 +8,7 @@ import {
TemplateResult, TemplateResult,
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { canRun, ScriptEntity } from "../../../data/script"; import { canRun, ScriptEntity } from "../../../data/script";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
@ -65,7 +65,7 @@ class HuiScriptEntityRow extends LitElement implements LovelaceRow {
${stateObj.state === "off" || stateObj.attributes.max ${stateObj.state === "off" || stateObj.attributes.max
? html`<mwc-button ? html`<mwc-button
@click=${this._runScript} @click=${this._runScript}
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state) || .disabled=${isUnavailableState(stateObj.state) ||
!canRun(stateObj)} !canRun(stateObj)}
> >
${this._config.action_name || ${this._config.action_name ||

View File

@ -8,7 +8,7 @@ import {
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { ActionHandlerEvent } from "../../../data/lovelace"; import { ActionHandlerEvent } from "../../../data/lovelace";
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor"; import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
@ -70,8 +70,7 @@ class HuiSensorEntityRow extends LitElement implements LovelaceRow {
})} })}
> >
${stateObj.attributes.device_class === ${stateObj.attributes.device_class ===
SENSOR_DEVICE_CLASS_TIMESTAMP && SENSOR_DEVICE_CLASS_TIMESTAMP && !isUnavailableState(stateObj.state)
!UNAVAILABLE_STATES.includes(stateObj.state)
? html` ? html`
<hui-timestamp-display <hui-timestamp-display
.hass=${this.hass} .hass=${this.hass}

View File

@ -1,6 +1,6 @@
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState, UNAVAILABLE } from "../../../data/entity";
import { TextEntity, setValue } from "../../../data/text"; import { TextEntity, setValue } from "../../../data/text";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
@ -71,7 +71,7 @@ class HuiTextEntityRow extends LitElement implements LovelaceRow {
const newValue = ev.target.value; const newValue = ev.target.value;
// Filter out invalid text states // Filter out invalid text states
if (newValue && UNAVAILABLE_STATES.includes(newValue)) { if (newValue && isUnavailableState(newValue)) {
ev.target.value = stateObj.state; ev.target.value = stateObj.state;
return; return;
} }

View File

@ -2,7 +2,7 @@ import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import "../../../components/entity/ha-entity-toggle"; import "../../../components/entity/ha-entity-toggle";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row"; import "../components/hui-generic-entity-row";
@ -44,7 +44,7 @@ class HuiToggleEntityRow extends LitElement implements LovelaceRow {
const showToggle = const showToggle =
stateObj.state === "on" || stateObj.state === "on" ||
stateObj.state === "off" || stateObj.state === "off" ||
UNAVAILABLE_STATES.includes(stateObj.state); isUnavailableState(stateObj.state);
return html` return html`
<hui-generic-entity-row <hui-generic-entity-row

View File

@ -13,7 +13,7 @@ import { computeStateDisplay } from "../../../common/entity/compute_state_displa
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import { formatNumber } from "../../../common/number/format_number"; import { formatNumber } from "../../../common/number/format_number";
import "../../../components/entity/state-badge"; import "../../../components/entity/state-badge";
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import { ActionHandlerEvent } from "../../../data/lovelace"; import { ActionHandlerEvent } from "../../../data/lovelace";
import { import {
getSecondaryWeatherAttribute, getSecondaryWeatherAttribute,
@ -141,7 +141,7 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
})} })}
> >
<div> <div>
${UNAVAILABLE_STATES.includes(stateObj.state) || ${isUnavailableState(stateObj.state) ||
stateObj.attributes.temperature === undefined || stateObj.attributes.temperature === undefined ||
stateObj.attributes.temperature === null stateObj.attributes.temperature === null
? computeStateDisplay( ? computeStateDisplay(

View File

@ -7,7 +7,7 @@ import { computeDomain } from "../common/entity/compute_domain";
import { computeStateDisplay } from "../common/entity/compute_state_display"; import { computeStateDisplay } from "../common/entity/compute_state_display";
import { computeRTL } from "../common/util/compute_rtl"; import { computeRTL } from "../common/util/compute_rtl";
import "../components/entity/state-info"; import "../components/entity/state-info";
import { UNAVAILABLE_STATES } from "../data/entity"; import { isUnavailableState } from "../data/entity";
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../data/sensor"; import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../data/sensor";
import "../panels/lovelace/components/hui-timestamp-display"; import "../panels/lovelace/components/hui-timestamp-display";
import { haStyle } from "../resources/styles"; import { haStyle } from "../resources/styles";
@ -42,7 +42,7 @@ export class StateCardDisplay extends LitElement {
${computeDomain(this.stateObj.entity_id) === "sensor" && ${computeDomain(this.stateObj.entity_id) === "sensor" &&
this.stateObj.attributes.device_class === this.stateObj.attributes.device_class ===
SENSOR_DEVICE_CLASS_TIMESTAMP && SENSOR_DEVICE_CLASS_TIMESTAMP &&
!UNAVAILABLE_STATES.includes(this.stateObj.state) !isUnavailableState(this.stateObj.state)
? html` <hui-timestamp-display ? html` <hui-timestamp-display
.hass=${this.hass} .hass=${this.hass}
.ts=${new Date(this.stateObj.state)} .ts=${new Date(this.stateObj.state)}

View File

@ -4,7 +4,7 @@ import { CSSResultGroup, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import "../components/entity/ha-entity-toggle"; import "../components/entity/ha-entity-toggle";
import "../components/entity/state-info"; import "../components/entity/state-info";
import { UNAVAILABLE_STATES } from "../data/entity"; import { isUnavailableState } from "../data/entity";
import { canRun, ScriptEntity } from "../data/script"; import { canRun, ScriptEntity } from "../data/script";
import { haStyle } from "../resources/styles"; import { haStyle } from "../resources/styles";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
@ -41,7 +41,7 @@ export class StateCardScript extends LitElement {
${stateObj.state === "off" || stateObj.attributes.max ${stateObj.state === "off" || stateObj.attributes.max
? html`<mwc-button ? html`<mwc-button
@click=${this._runScript} @click=${this._runScript}
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state) || .disabled=${isUnavailableState(stateObj.state) ||
!canRun(stateObj)} !canRun(stateObj)}
> >
${this.hass!.localize("ui.card.script.run")} ${this.hass!.localize("ui.card.script.run")}

View File

@ -4,7 +4,7 @@ import { customElement, property } from "lit/decorators";
import { computeStateName } from "../common/entity/compute_state_name"; import { computeStateName } from "../common/entity/compute_state_name";
import { stopPropagation } from "../common/dom/stop_propagation"; import { stopPropagation } from "../common/dom/stop_propagation";
import "../components/entity/state-badge"; import "../components/entity/state-badge";
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../data/entity"; import { isUnavailableState, UNAVAILABLE } from "../data/entity";
import { TextEntity, setValue } from "../data/text"; import { TextEntity, setValue } from "../data/text";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
@ -37,7 +37,7 @@ class StateCardText extends LitElement {
const value = ev.target.value; const value = ev.target.value;
// Filter out invalid text states // Filter out invalid text states
if (value && UNAVAILABLE_STATES.includes(value)) { if (value && isUnavailableState(value)) {
ev.target.value = this.stateObj.state; ev.target.value = this.stateObj.state;
return; return;
} }