diff --git a/src/common/entity/color/climate_color.ts b/src/common/entity/color/climate_color.ts index 9d7d17ef6a..390e9ab739 100644 --- a/src/common/entity/color/climate_color.ts +++ b/src/common/entity/color/climate_color.ts @@ -3,6 +3,7 @@ import { HvacAction } from "../../../data/climate"; export const CLIMATE_HVAC_ACTION_COLORS: Record = { cooling: "var(--rgb-state-climate-cool-color)", drying: "var(--rgb-state-climate-dry-color)", + fan: "var(--rgb-state-climate-fan-only-color)", heating: "var(--rgb-state-climate-heat-color)", idle: "var(--rgb-state-climate-idle-color)", off: "var(--rgb-state-climate-off-color)", diff --git a/src/common/entity/get_states.ts b/src/common/entity/get_states.ts index 92c1365b14..f79a16e5e2 100644 --- a/src/common/entity/get_states.ts +++ b/src/common/entity/get_states.ts @@ -2,7 +2,7 @@ import { HassEntity } from "home-assistant-js-websocket"; import { computeStateDomain } from "./compute_state_domain"; import { UNAVAILABLE_STATES } from "../../data/entity"; -const FIXED_DOMAIN_STATES = { +export const FIXED_DOMAIN_STATES = { alarm_control_panel: [ "armed_away", "armed_custom_bypass", @@ -57,7 +57,7 @@ const FIXED_DOMAIN_STATES = { "windy-variant", "windy", ], -}; +} as const; const FIXED_DOMAIN_ATTRIBUTE_STATES = { alarm_control_panel: { diff --git a/src/common/entity/state_active.ts b/src/common/entity/state_active.ts index 2c673f3a6b..af5f75d126 100644 --- a/src/common/entity/state_active.ts +++ b/src/common/entity/state_active.ts @@ -1,5 +1,5 @@ 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"; export function stateActive(stateObj: HassEntity, state?: string): boolean { @@ -10,7 +10,7 @@ export function stateActive(stateObj: HassEntity, state?: string): boolean { return compareState !== UNAVAILABLE; } - if (UNAVAILABLE_STATES.includes(compareState)) { + if (isUnavailableState(compareState)) { return false; } diff --git a/src/common/translations/localize.ts b/src/common/translations/localize.ts index 0b5fc77030..5a38952c9a 100644 --- a/src/common/translations/localize.ts +++ b/src/common/translations/localize.ts @@ -12,9 +12,6 @@ import { getLocalLanguage } from "../../util/common-translation"; export type LocalizeKeys = | FlattenObjectKeys> | `panel.${string}` - | `state.${string}` - | `state_attributes.${string}` - | `state_badge.${string}` | `ui.card.alarm_control_panel.${string}` | `ui.card.weather.attributes.${string}` | `ui.card.weather.cardinal_direction.${string}` diff --git a/src/components/entity/ha-entity-toggle.ts b/src/components/entity/ha-entity-toggle.ts index 0bdf2e3126..223b208e1e 100644 --- a/src/components/entity/ha-entity-toggle.ts +++ b/src/components/entity/ha-entity-toggle.ts @@ -12,7 +12,7 @@ import { property, state } from "lit/decorators"; import { STATES_OFF } from "../../common/const"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; 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 { HomeAssistant } from "../../types"; import "../ha-formfield"; @@ -22,7 +22,7 @@ import "../ha-switch"; const isOn = (stateObj?: HassEntity) => stateObj !== undefined && !STATES_OFF.includes(stateObj.state) && - !UNAVAILABLE_STATES.includes(stateObj.state); + !isUnavailableState(stateObj.state); export class HaEntityToggle extends LitElement { // hass is not a property so that we only re-render on stateObj changes diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts index 3c7cc3dfa7..35fa54748e 100644 --- a/src/components/entity/ha-state-label-badge.ts +++ b/src/components/entity/ha-state-label-badge.ts @@ -10,21 +10,45 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; +import { arrayLiteralIncludes } from "../../common/array/literal-includes"; import secondsToDuration from "../../common/datetime/seconds_to_duration"; import { computeStateDisplay } from "../../common/entity/compute_state_display"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; +import { FIXED_DOMAIN_STATES } from "../../common/entity/get_states"; import { formatNumber, getNumberFormatOptions, isNumericState, } from "../../common/number/format_number"; -import { UNAVAILABLE, UNKNOWN } from "../../data/entity"; +import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../data/entity"; import { timerTimeRemaining } from "../../data/timer"; import { HomeAssistant } from "../../types"; import "../ha-label-badge"; 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; + +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") export class HaStateLabelBadge extends LitElement { @property({ attribute: false }) public hass?: HomeAssistant; @@ -186,19 +210,18 @@ export class HaStateLabelBadge extends LitElement { } } - private _computeLabel(domain, entityState, _timerTimeRemaining) { - if ( - entityState.state === UNAVAILABLE || - ["device_tracker", "alarm_control_panel", "person"].includes(domain) - ) { - // Localize the state with a special state_badge namespace, which has variations of - // the state translations that are truncated to fit within the badge label. Translations - // are only added for device_tracker, alarm_control_panel and person. - return ( - this.hass!.localize(`state_badge.${domain}.${entityState.state}`) || - this.hass!.localize(`state_badge.default.${entityState.state}`) || - entityState.state - ); + private _computeLabel( + domain: string, + entityState: HassEntity, + _timerTimeRemaining = 0 + ) { + // For unavailable states or certain domains, use a special translation that is truncated to fit within the badge label + if (isUnavailableState(entityState.state)) { + return this.hass!.localize(`state_badge.default.${entityState.state}`); + } + const domainStateKey = getTruncatedKey(domain, entityState.state); + if (domainStateKey) { + return this.hass!.localize(`state_badge.${domainStateKey}`); } if (domain === "timer") { return secondsToDuration(_timerTimeRemaining); diff --git a/src/components/ha-climate-state.ts b/src/components/ha-climate-state.ts index 52ecb1de7f..8b9f170b00 100644 --- a/src/components/ha-climate-state.ts +++ b/src/components/ha-climate-state.ts @@ -1,22 +1,21 @@ -import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import { formatNumber } from "../common/number/format_number"; -import { CLIMATE_PRESET_NONE } from "../data/climate"; -import { UNAVAILABLE_STATES } from "../data/entity"; +import { ClimateEntity, CLIMATE_PRESET_NONE } from "../data/climate"; +import { isUnavailableState } from "../data/entity"; import type { HomeAssistant } from "../types"; @customElement("ha-climate-state") class HaClimateState extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public stateObj!: HassEntity; + @property({ attribute: false }) public stateObj!: ClimateEntity; protected render(): TemplateResult { const currentStatus = this._computeCurrentStatus(); return html`
- ${!UNAVAILABLE_STATES.includes(this.stateObj.state) + ${!isUnavailableState(this.stateObj.state) ? html` ${this._localizeState()} ${this.stateObj.attributes.preset_mode && @@ -31,7 +30,7 @@ class HaClimateState extends LitElement { : this._localizeState()}
- ${currentStatus && !UNAVAILABLE_STATES.includes(this.stateObj.state) + ${currentStatus && !isUnavailableState(this.stateObj.state) ? html`
${this.hass.localize("ui.card.climate.currently")}:
${currentStatus}
@@ -109,7 +108,7 @@ class HaClimateState extends LitElement { } private _localizeState(): string { - if (UNAVAILABLE_STATES.includes(this.stateObj.state)) { + if (isUnavailableState(this.stateObj.state)) { return this.hass.localize(`state.default.${this.stateObj.state}`); } diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index b27f48385d..3a760c89d9 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -27,7 +27,7 @@ import { until } from "lit/directives/until"; import { fireEvent } from "../../common/dom/fire_event"; import { computeRTLDirection } from "../../common/util/compute_rtl"; 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 { browseMediaPlayer, @@ -247,7 +247,7 @@ export class HaMediaPlayerBrowse extends LitElement { }); } else if ( err.code === "entity_not_found" && - UNAVAILABLE_STATES.includes(this.hass.states[this.entityId]?.state) + isUnavailableState(this.hass.states[this.entityId]?.state) ) { this._setError({ message: this.hass.localize( diff --git a/src/data/calendar.ts b/src/data/calendar.ts index 2cd6b0b944..0b856de9b9 100644 --- a/src/data/calendar.ts +++ b/src/data/calendar.ts @@ -2,7 +2,7 @@ import { getColorByIndex } from "../common/color/colors"; import { computeDomain } from "../common/entity/compute_domain"; import { computeStateName } from "../common/entity/compute_state_name"; import type { HomeAssistant } from "../types"; -import { UNAVAILABLE_STATES } from "./entity"; +import { isUnavailableState } from "./entity"; export interface Calendar { entity_id: string; @@ -138,7 +138,7 @@ export const getCalendars = (hass: HomeAssistant): Calendar[] => .filter( (eid) => computeDomain(eid) === "calendar" && - !UNAVAILABLE_STATES.includes(hass.states[eid].state) + !isUnavailableState(hass.states[eid].state) ) .sort() .map((eid, idx) => ({ diff --git a/src/data/climate.ts b/src/data/climate.ts index a6fbea3bae..064fb32c98 100644 --- a/src/data/climate.ts +++ b/src/data/climate.ts @@ -2,6 +2,7 @@ import { HassEntityAttributeBase, HassEntityBase, } from "home-assistant-js-websocket"; +import { TranslationDict } from "../types"; export type HvacMode = | "off" @@ -14,7 +15,12 @@ export type HvacMode = 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 & { attributes: HassEntityAttributeBase & { @@ -34,10 +40,10 @@ export type ClimateEntity = HassEntityBase & { target_humidity_high?: number; min_humidity?: number; max_humidity?: number; - fan_mode?: string; - fan_modes?: string[]; - preset_mode?: string; - preset_modes?: string[]; + fan_mode?: FanMode; + fan_modes?: FanMode[]; + preset_mode?: PresetMode; + preset_modes?: PresetMode[]; swing_mode?: string; swing_modes?: string[]; aux_heat?: "on" | "off"; diff --git a/src/data/entity.ts b/src/data/entity.ts index dbb7f759cc..fe6ca3d6dc 100644 --- a/src/data/entity.ts +++ b/src/data/entity.ts @@ -1,7 +1,12 @@ +import { arrayLiteralIncludes } from "../common/array/literal-includes"; + export const UNAVAILABLE = "unavailable"; export const UNKNOWN = "unknown"; export const ON = "on"; export const OFF = "off"; -export const UNAVAILABLE_STATES = [UNAVAILABLE, UNKNOWN]; -export const OFF_STATES = [UNAVAILABLE, UNKNOWN, OFF]; +export const UNAVAILABLE_STATES = [UNAVAILABLE, UNKNOWN] as const; +export const OFF_STATES = [UNAVAILABLE, UNKNOWN, OFF] as const; + +export const isUnavailableState = arrayLiteralIncludes(UNAVAILABLE_STATES); +export const isOffState = arrayLiteralIncludes(OFF_STATES); diff --git a/src/data/humidifier.ts b/src/data/humidifier.ts index 968aad1dcd..5f0fed7a5f 100644 --- a/src/data/humidifier.ts +++ b/src/data/humidifier.ts @@ -2,14 +2,24 @@ import { HassEntityAttributeBase, HassEntityBase, } 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 & { + state: HumidifierState; attributes: HassEntityAttributeBase & { humidity?: number; min_humidity?: number; max_humidity?: number; - mode?: string; - available_modes?: string[]; + mode?: HumidifierMode; + available_modes?: HumidifierMode[]; }; }; diff --git a/src/data/media-player.ts b/src/data/media-player.ts index 1bf35025fc..69c03646ec 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -35,7 +35,7 @@ import type { import { supportsFeature } from "../common/entity/supports-feature"; import { MediaPlayerItemId } from "../components/media-player/ha-media-player-browse"; import type { HomeAssistant, TranslationDict } from "../types"; -import { UNAVAILABLE_STATES } from "./entity"; +import { isUnavailableState } from "./entity"; import { isTTSMediaSource } from "./tts"; interface MediaPlayerEntityAttributes extends HassEntityAttributeBase { @@ -259,7 +259,7 @@ export const computeMediaControls = ( const state = stateObj.state; - if (UNAVAILABLE_STATES.includes(state)) { + if (isUnavailableState(state)) { return undefined; } diff --git a/src/dialogs/more-info/controls/more-info-automation.ts b/src/dialogs/more-info/controls/more-info-automation.ts index fab8cfb54b..880c29437f 100644 --- a/src/dialogs/more-info/controls/more-info-automation.ts +++ b/src/dialogs/more-info/controls/more-info-automation.ts @@ -4,7 +4,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import "../../../components/ha-relative-time"; import { triggerAutomationActions } from "../../../data/automation"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { HomeAssistant } from "../../../types"; @customElement("more-info-automation") @@ -32,7 +32,7 @@ class MoreInfoAutomation extends LitElement {
${this.hass.localize("ui.card.automation.trigger")} diff --git a/src/dialogs/more-info/controls/more-info-counter.ts b/src/dialogs/more-info/controls/more-info-counter.ts index a33fba0c90..a9e57fe679 100644 --- a/src/dialogs/more-info/controls/more-info-counter.ts +++ b/src/dialogs/more-info/controls/more-info-counter.ts @@ -2,7 +2,7 @@ import "@material/mwc-button"; import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { HomeAssistant } from "../../../types"; @customElement("more-info-counter") @@ -16,7 +16,7 @@ class MoreInfoCounter extends LitElement { return html``; } - const disabled = UNAVAILABLE_STATES.includes(this.stateObj!.state); + const disabled = isUnavailableState(this.stateObj!.state); return html`
diff --git a/src/dialogs/more-info/controls/more-info-input_datetime.ts b/src/dialogs/more-info/controls/more-info-input_datetime.ts index fbe6917fd6..bd5a8cd8e4 100644 --- a/src/dialogs/more-info/controls/more-info-input_datetime.ts +++ b/src/dialogs/more-info/controls/more-info-input_datetime.ts @@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import "../../../components/ha-date-input"; import "../../../components/ha-time-input"; -import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; +import { isUnavailableState, UNKNOWN } from "../../../data/entity"; import { setInputDateTimeValue, stateToIsoDateString, @@ -28,7 +28,7 @@ class MoreInfoInputDatetime extends LitElement { @@ -45,7 +45,7 @@ class MoreInfoInputDatetime extends LitElement { ? this.stateObj.state.split(" ")[1] : this.stateObj.state} .locale=${this.hass.locale} - .disabled=${UNAVAILABLE_STATES.includes(this.stateObj.state)} + .disabled=${isUnavailableState(this.stateObj.state)} @value-changed=${this._timeChanged} @click=${this._stopEventPropagation} > diff --git a/src/dialogs/more-info/controls/more-info-update.ts b/src/dialogs/more-info/controls/more-info-update.ts index 7801e69730..de73cc1f3c 100644 --- a/src/dialogs/more-info/controls/more-info-update.ts +++ b/src/dialogs/more-info/controls/more-info-update.ts @@ -9,7 +9,7 @@ import "../../../components/ha-checkbox"; import "../../../components/ha-circular-progress"; import "../../../components/ha-formfield"; import "../../../components/ha-markdown"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { UpdateEntity, updateIsInstalling, @@ -37,7 +37,7 @@ class MoreInfoUpdate extends LitElement { if ( !this.hass || !this.stateObj || - UNAVAILABLE_STATES.includes(this.stateObj.state) + isUnavailableState(this.stateObj.state) ) { return html``; } diff --git a/src/panels/config/scene/ha-scene-dashboard.ts b/src/panels/config/scene/ha-scene-dashboard.ts index 652420dca0..7146903b3c 100644 --- a/src/panels/config/scene/ha-scene-dashboard.ts +++ b/src/panels/config/scene/ha-scene-dashboard.ts @@ -45,7 +45,7 @@ import { showToast } from "../../../util/toast"; import { configSections } from "../ha-panel-config"; import { formatShortDateTime } from "../../../common/datetime/format_date_time"; import { relativeTime } from "../../../common/datetime/relative_time"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; @customElement("ha-scene-dashboard") class HaSceneDashboard extends LitElement { @@ -116,7 +116,7 @@ class HaSceneDashboard extends LitElement { const now = new Date(); const dayDifference = differenceInDays(now, date); return html` - ${last_activated && !UNAVAILABLE_STATES.includes(last_activated) + ${last_activated && !isUnavailableState(last_activated) ? dayDifference > 3 ? formatShortDateTime(date, this.hass.locale) : relativeTime(date, this.hass.locale) diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index a2bf35cbbe..adfcbe04c1 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -42,7 +42,7 @@ import { DeviceRegistryEntry, subscribeDeviceRegistry, } from "../../../data/device_registry"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { EntityRegistryEntry, subscribeEntityRegistry, @@ -181,8 +181,7 @@ export class HuiAreaCard : entities ).some( (entity) => - !UNAVAILABLE_STATES.includes(entity.state) && - !STATES_OFF.includes(entity.state) + !isUnavailableState(entity.state) && !STATES_OFF.includes(entity.state) ); } diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts index 6065c116f5..a0abc1a25c 100644 --- a/src/panels/lovelace/cards/hui-entity-card.ts +++ b/src/panels/lovelace/cards/hui-entity-card.ts @@ -27,7 +27,7 @@ import { import { iconColorCSS } from "../../../common/style/icon_color_css"; import "../../../components/ha-card"; import "../../../components/ha-icon"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { formatAttributeValue } from "../../../data/entity_attributes"; import { LightEntity } from "../../../data/light"; import { HomeAssistant } from "../../../types"; @@ -130,7 +130,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { const domain = computeStateDomain(stateObj); const showUnit = this._config.attribute ? this._config.attribute in stateObj.attributes - : !UNAVAILABLE_STATES.includes(stateObj.state); + : !isUnavailableState(stateObj.state); const name = this._config.name || computeStateName(stateObj); diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index 7c1668d7b8..6678ced43a 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -17,7 +17,7 @@ import "../../../components/entity/state-badge"; import "../../../components/ha-card"; import "../../../components/ha-icon"; import "../../../components/ha-relative-time"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { ActionHandlerEvent, CallServiceActionConfig, @@ -315,7 +315,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { ${computeDomain(entityConf.entity) === "sensor" && stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP && - !UNAVAILABLE_STATES.includes(stateObj.state) + !isUnavailableState(stateObj.state) ? html` ` : html` - ${UNAVAILABLE_STATES.includes(stateObj.state) || + ${isUnavailableState(stateObj.state) || setHumidity === undefined || setHumidity === null ? "" @@ -132,8 +132,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { ${this.hass!.localize(`state.default.${stateObj.state}`)} - ${stateObj.attributes.mode && - !UNAVAILABLE_STATES.includes(stateObj.state) + ${stateObj.attributes.mode && !isUnavailableState(stateObj.state) ? html` - ${this.hass!.localize( @@ -161,7 +160,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
@@ -225,7 +224,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { } private _getSetHum(stateObj: HassEntity): undefined | number { - if (UNAVAILABLE_STATES.includes(stateObj.state)) { + if (isUnavailableState(stateObj.state)) { return undefined; } diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index c1410dbc56..2b60d831cb 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -18,7 +18,7 @@ import { computeStateName } from "../../../common/entity/compute_state_name"; import "../../../components/ha-card"; import "../../../components/ha-icon-button"; 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 { ActionHandlerEvent } from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; @@ -118,7 +118,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { min="1" max="100" .value=${brightness} - .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} + .disabled=${isUnavailableState(stateObj.state)} @value-changing=${this._dragEvent} @value-changed=${this._setBrightness} style=${styleMap({ @@ -133,7 +133,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { "state-on": stateObj.state === "on", "state-unavailable": stateObj.state === UNAVAILABLE, })}" - .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} + .disabled=${isUnavailableState(stateObj.state)} style=${styleMap({ filter: this._computeBrightness(stateObj), color: this._computeColor(stateObj), @@ -154,7 +154,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
- ${UNAVAILABLE_STATES.includes(stateObj.state) + ${isUnavailableState(stateObj.state) ? html`
${computeStateDisplay( diff --git a/src/panels/lovelace/cards/hui-media-control-card.ts b/src/panels/lovelace/cards/hui-media-control-card.ts index 481daffa73..8f2f47303f 100644 --- a/src/panels/lovelace/cards/hui-media-control-card.ts +++ b/src/panels/lovelace/cards/hui-media-control-card.ts @@ -22,7 +22,7 @@ import "../../../components/ha-card"; import "../../../components/ha-icon-button"; import "../../../components/ha-state-icon"; import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { cleanupMediaTitle, computeMediaControls, @@ -173,7 +173,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard { const isOffState = entityState === "off"; const isUnavailable = - UNAVAILABLE_STATES.includes(entityState) || + isUnavailableState(entityState) || (entityState === "off" && !supportsFeature(stateObj, SUPPORT_TURN_ON)); const hasNoImage = !this._image; const controls = computeMediaControls(stateObj, false); diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index 4ac603bbfe..9d74391b99 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -20,7 +20,7 @@ import "../../../components/tile/ha-tile-image"; import "../../../components/tile/ha-tile-info"; import { cameraUrlWithWidthHeight } from "../../../data/camera"; import { CoverEntity } from "../../../data/cover"; -import { ON, UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState, ON } from "../../../data/entity"; import { FanEntity } from "../../../data/fan"; import { LightEntity } from "../../../data/light"; import { ActionHandlerEvent } from "../../../data/lovelace"; @@ -171,7 +171,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard { if ( (stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP || TIMESTAMP_STATE_DOMAINS.includes(domain)) && - !UNAVAILABLE_STATES.includes(stateObj.state) + !isUnavailableState(stateObj.state) ) { return html` = { cooling: mdiSnowflake, drying: mdiWaterPercent, + fan: mdiFan, heating: mdiFire, idle: mdiClockOutline, off: mdiPower, diff --git a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts index df8cd17670..9de121f0dc 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts @@ -17,7 +17,7 @@ import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/search-input"; import "../../../../components/ha-circular-progress"; -import { UNAVAILABLE_STATES } from "../../../../data/entity"; +import { isUnavailableState } from "../../../../data/entity"; import type { LovelaceCardConfig, LovelaceConfig, @@ -163,12 +163,12 @@ export class HuiCardPicker extends LitElement { this._usedEntities = [...usedEntities].filter( (eid) => this.hass!.states[eid] && - !UNAVAILABLE_STATES.includes(this.hass!.states[eid].state) + !isUnavailableState(this.hass!.states[eid].state) ); this._unusedEntities = [...unusedEntities].filter( (eid) => this.hass!.states[eid] && - !UNAVAILABLE_STATES.includes(this.hass!.states[eid].state) + !isUnavailableState(this.hass!.states[eid].state) ); this._loadCards(); diff --git a/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts b/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts index 9170e34ebd..5b2b91154b 100644 --- a/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts @@ -1,6 +1,7 @@ import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import "../../../components/entity/ha-entity-toggle"; +import { HumidifierEntity } from "../../../data/humidifier"; import { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import "../components/hui-generic-entity-row"; @@ -30,7 +31,7 @@ class HuiHumidifierEntityRow extends LitElement implements LovelaceRow { return html``; } - const stateObj = this.hass.states[this._config.entity]; + const stateObj = this.hass.states[this._config.entity] as HumidifierEntity; if (!stateObj) { return html` diff --git a/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts index 93609b883f..2fb6e23fd0 100644 --- a/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts @@ -8,7 +8,7 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import "../../../components/ha-date-input"; -import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; +import { isUnavailableState, UNKNOWN } from "../../../data/entity"; import { setInputDateTimeValue, stateToIsoDateString, @@ -67,7 +67,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow { @@ -83,7 +83,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow { ? stateObj.state.split(" ")[1] : stateObj.state} .locale=${this.hass.locale} - .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} + .disabled=${isUnavailableState(stateObj.state)} @value-changed=${this._timeChanged} @click=${this._stopEventPropagation} > diff --git a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts index 1cdd25958c..e20bf2bb9f 100644 --- a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts @@ -12,7 +12,7 @@ import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { debounce } from "../../../common/util/debounce"; import "../../../components/ha-slider"; import "../../../components/ha-textfield"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { setValue } from "../../../data/input_text"; import { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; @@ -85,7 +85,7 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow { ? html`
${stateObj.state === "locked" diff --git a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts index 68e5526c50..2e1fa726cc 100644 --- a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts @@ -27,7 +27,7 @@ import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { debounce } from "../../../common/util/debounce"; import "../../../components/ha-icon-button"; import "../../../components/ha-slider"; -import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; +import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../../data/entity"; import { computeMediaDescription, ControlButton, @@ -203,7 +203,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
${supportsFeature(stateObj, SUPPORT_TURN_ON) && entityState === "off" && - !UNAVAILABLE_STATES.includes(entityState) + !isUnavailableState(entityState) ? html` ${this._config.action_name || diff --git a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts index 297bf97aeb..c5fc4c83ff 100644 --- a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts @@ -8,7 +8,7 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; 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 { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor"; import { HomeAssistant } from "../../../types"; @@ -70,8 +70,7 @@ class HuiSensorEntityRow extends LitElement implements LovelaceRow { })} > ${stateObj.attributes.device_class === - SENSOR_DEVICE_CLASS_TIMESTAMP && - !UNAVAILABLE_STATES.includes(stateObj.state) + SENSOR_DEVICE_CLASS_TIMESTAMP && !isUnavailableState(stateObj.state) ? html`
- ${UNAVAILABLE_STATES.includes(stateObj.state) || + ${isUnavailableState(stateObj.state) || stateObj.attributes.temperature === undefined || stateObj.attributes.temperature === null ? computeStateDisplay( diff --git a/src/state-summary/state-card-display.ts b/src/state-summary/state-card-display.ts index 0d42269577..956d90cd7b 100755 --- a/src/state-summary/state-card-display.ts +++ b/src/state-summary/state-card-display.ts @@ -7,7 +7,7 @@ import { computeDomain } from "../common/entity/compute_domain"; import { computeStateDisplay } from "../common/entity/compute_state_display"; import { computeRTL } from "../common/util/compute_rtl"; 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 "../panels/lovelace/components/hui-timestamp-display"; import { haStyle } from "../resources/styles"; @@ -42,7 +42,7 @@ export class StateCardDisplay extends LitElement { ${computeDomain(this.stateObj.entity_id) === "sensor" && this.stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP && - !UNAVAILABLE_STATES.includes(this.stateObj.state) + !isUnavailableState(this.stateObj.state) ? html` ${this.hass!.localize("ui.card.script.run")} diff --git a/src/state-summary/state-card-text.ts b/src/state-summary/state-card-text.ts index 5ef0456b95..563d055d4c 100644 --- a/src/state-summary/state-card-text.ts +++ b/src/state-summary/state-card-text.ts @@ -4,7 +4,7 @@ import { customElement, property } from "lit/decorators"; import { computeStateName } from "../common/entity/compute_state_name"; import { stopPropagation } from "../common/dom/stop_propagation"; 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 type { HomeAssistant } from "../types"; @@ -37,7 +37,7 @@ class StateCardText extends LitElement { const value = ev.target.value; // Filter out invalid text states - if (value && UNAVAILABLE_STATES.includes(value)) { + if (value && isUnavailableState(value)) { ev.target.value = this.stateObj.state; return; }