diff --git a/src/common/entity/compute_attribute_display.ts b/src/common/entity/compute_attribute_display.ts
new file mode 100644
index 0000000000..f969646265
--- /dev/null
+++ b/src/common/entity/compute_attribute_display.ts
@@ -0,0 +1,52 @@
+import { HassEntity } from "home-assistant-js-websocket";
+import { EntityRegistryEntry } from "../../data/entity_registry";
+import { HomeAssistant } from "../../types";
+import { LocalizeFunc } from "../translations/localize";
+import { computeDomain } from "./compute_domain";
+
+export const computeAttributeValueDisplay = (
+ localize: LocalizeFunc,
+ stateObj: HassEntity,
+ entities: HomeAssistant["entities"],
+ attribute: string,
+ value?: any
+): string => {
+ const entityId = stateObj.entity_id;
+ const attributeValue =
+ value !== undefined ? value : stateObj.attributes[attribute];
+ const domain = computeDomain(entityId);
+ const entity = entities[entityId] as EntityRegistryEntry | undefined;
+ const translationKey = entity?.translation_key;
+
+ return (
+ (translationKey &&
+ localize(
+ `component.${entity.platform}.entity.${domain}.${translationKey}.state_attributes.${attribute}.state.${attributeValue}`
+ )) ||
+ localize(
+ `component.${domain}.state_attributes._.${attribute}.state.${attributeValue}`
+ ) ||
+ attributeValue
+ );
+};
+
+export const computeAttributeNameDisplay = (
+ localize: LocalizeFunc,
+ stateObj: HassEntity,
+ entities: HomeAssistant["entities"],
+ attribute: string
+): string => {
+ const entityId = stateObj.entity_id;
+ const domain = computeDomain(entityId);
+ const entity = entities[entityId] as EntityRegistryEntry | undefined;
+ const translationKey = entity?.translation_key;
+
+ return (
+ (translationKey &&
+ localize(
+ `component.${entity.platform}.entity.${domain}.${translationKey}.state_attributes.${attribute}.name`
+ )) ||
+ localize(`component.${domain}.state_attributes._.${attribute}.name`) ||
+ attribute
+ );
+};
diff --git a/src/components/ha-climate-state.ts b/src/components/ha-climate-state.ts
index 8b9f170b00..9443352fb5 100644
--- a/src/components/ha-climate-state.ts
+++ b/src/components/ha-climate-state.ts
@@ -1,5 +1,7 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
+import { computeAttributeValueDisplay } from "../common/entity/compute_attribute_display";
+import { computeStateDisplay } from "../common/entity/compute_state_display";
import { formatNumber } from "../common/number/format_number";
import { ClimateEntity, CLIMATE_PRESET_NONE } from "../data/climate";
import { isUnavailableState } from "../data/entity";
@@ -21,9 +23,12 @@ class HaClimateState extends LitElement {
${this.stateObj.attributes.preset_mode &&
this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
? html`-
- ${this.hass.localize(
- `state_attributes.climate.preset_mode.${this.stateObj.attributes.preset_mode}`
- ) || this.stateObj.attributes.preset_mode}`
+ ${computeAttributeValueDisplay(
+ this.hass.localize,
+ this.stateObj,
+ this.hass.entities,
+ "preset_mode"
+ )}`
: ""}
${this._computeTarget()}
`
@@ -112,13 +117,19 @@ class HaClimateState extends LitElement {
return this.hass.localize(`state.default.${this.stateObj.state}`);
}
- const stateString = this.hass.localize(
- `component.climate.state._.${this.stateObj.state}`
+ const stateString = computeStateDisplay(
+ this.hass.localize,
+ this.stateObj,
+ this.hass.locale,
+ this.hass.entities
);
return this.stateObj.attributes.hvac_action
- ? `${this.hass.localize(
- `state_attributes.climate.hvac_action.${this.stateObj.attributes.hvac_action}`
+ ? `${computeAttributeValueDisplay(
+ this.hass.localize,
+ this.stateObj,
+ this.hass.entities,
+ "hvac_action"
)} (${stateString})`
: stateString;
}
diff --git a/src/data/climate.ts b/src/data/climate.ts
index 064fb32c98..6ed6fa7523 100644
--- a/src/data/climate.ts
+++ b/src/data/climate.ts
@@ -2,7 +2,6 @@ import {
HassEntityAttributeBase,
HassEntityBase,
} from "home-assistant-js-websocket";
-import { TranslationDict } from "../types";
export type HvacMode =
| "off"
@@ -15,12 +14,13 @@ export type HvacMode =
export const CLIMATE_PRESET_NONE = "none";
-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 HvacAction =
+ | "off"
+ | "heating"
+ | "cooling"
+ | "drying"
+ | "idle"
+ | "fan";
export type ClimateEntity = HassEntityBase & {
attributes: HassEntityAttributeBase & {
@@ -40,23 +40,25 @@ export type ClimateEntity = HassEntityBase & {
target_humidity_high?: number;
min_humidity?: number;
max_humidity?: number;
- fan_mode?: FanMode;
- fan_modes?: FanMode[];
- preset_mode?: PresetMode;
- preset_modes?: PresetMode[];
+ fan_mode?: string;
+ fan_modes?: string[];
+ preset_mode?: string;
+ preset_modes?: string[];
swing_mode?: string;
swing_modes?: string[];
aux_heat?: "on" | "off";
};
};
-export const CLIMATE_SUPPORT_TARGET_TEMPERATURE = 1;
-export const CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE = 2;
-export const CLIMATE_SUPPORT_TARGET_HUMIDITY = 4;
-export const CLIMATE_SUPPORT_FAN_MODE = 8;
-export const CLIMATE_SUPPORT_PRESET_MODE = 16;
-export const CLIMATE_SUPPORT_SWING_MODE = 32;
-export const CLIMATE_SUPPORT_AUX_HEAT = 64;
+export const enum ClimateEntityFeature {
+ TARGET_TEMPERATURE = 1,
+ TARGET_TEMPERATURE_RANGE = 2,
+ TARGET_HUMIDITY = 4,
+ FAN_MODE = 8,
+ PRESET_MODE = 16,
+ SWING_MODE = 32,
+ AUX_HEAT = 64,
+}
const hvacModeOrdering: { [key in HvacMode]: number } = {
auto: 1,
diff --git a/src/data/translation.ts b/src/data/translation.ts
index 56764f922a..1d3f5c2d71 100644
--- a/src/data/translation.ts
+++ b/src/data/translation.ts
@@ -44,6 +44,7 @@ declare global {
export type TranslationCategory =
| "title"
| "state"
+ | "state_attributes"
| "entity"
| "config"
| "config_panel"
diff --git a/src/dialogs/more-info/controls/more-info-climate.ts b/src/dialogs/more-info/controls/more-info-climate.ts
index 03b94c3827..559b9bfbef 100644
--- a/src/dialogs/more-info/controls/more-info-climate.ts
+++ b/src/dialogs/more-info/controls/more-info-climate.ts
@@ -11,6 +11,11 @@ import { property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event";
import { stopPropagation } from "../../../common/dom/stop_propagation";
+import {
+ computeAttributeNameDisplay,
+ computeAttributeValueDisplay,
+} from "../../../common/entity/compute_attribute_display";
+import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-climate-control";
@@ -19,13 +24,7 @@ import "../../../components/ha-slider";
import "../../../components/ha-switch";
import {
ClimateEntity,
- CLIMATE_SUPPORT_AUX_HEAT,
- CLIMATE_SUPPORT_FAN_MODE,
- CLIMATE_SUPPORT_PRESET_MODE,
- CLIMATE_SUPPORT_SWING_MODE,
- CLIMATE_SUPPORT_TARGET_HUMIDITY,
- CLIMATE_SUPPORT_TARGET_TEMPERATURE,
- CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE,
+ ClimateEntityFeature,
compareClimateHvacModes,
} from "../../../data/climate";
import { HomeAssistant } from "../../../types";
@@ -47,26 +46,32 @@ class MoreInfoClimate extends LitElement {
const supportTargetTemperature = supportsFeature(
stateObj,
- CLIMATE_SUPPORT_TARGET_TEMPERATURE
+ ClimateEntityFeature.TARGET_TEMPERATURE
);
const supportTargetTemperatureRange = supportsFeature(
stateObj,
- CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE
+ ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
);
const supportTargetHumidity = supportsFeature(
stateObj,
- CLIMATE_SUPPORT_TARGET_HUMIDITY
+ ClimateEntityFeature.TARGET_HUMIDITY
+ );
+ const supportFanMode = supportsFeature(
+ stateObj,
+ ClimateEntityFeature.FAN_MODE
);
- const supportFanMode = supportsFeature(stateObj, CLIMATE_SUPPORT_FAN_MODE);
const supportPresetMode = supportsFeature(
stateObj,
- CLIMATE_SUPPORT_PRESET_MODE
+ ClimateEntityFeature.PRESET_MODE
);
const supportSwingMode = supportsFeature(
stateObj,
- CLIMATE_SUPPORT_SWING_MODE
+ ClimateEntityFeature.SWING_MODE
+ );
+ const supportAuxHeat = supportsFeature(
+ stateObj,
+ ClimateEntityFeature.AUX_HEAT
);
- const supportAuxHeat = supportsFeature(stateObj, CLIMATE_SUPPORT_AUX_HEAT);
const temperatureStepSize =
stateObj.attributes.target_temp_step ||
@@ -94,7 +99,12 @@ class MoreInfoClimate extends LitElement {
${supportTargetTemperature || supportTargetTemperatureRange
? html`
- ${hass.localize("ui.card.climate.target_temperature")}
+ ${computeAttributeNameDisplay(
+ hass.localize,
+ stateObj,
+ hass.entities,
+ "temperature"
+ )}
`
: ""}
@@ -145,7 +155,14 @@ class MoreInfoClimate extends LitElement {
${supportTargetHumidity
? html`
-
${hass.localize("ui.card.climate.target_humidity")}
+
+ ${computeAttributeNameDisplay(
+ hass.localize,
+ stateObj,
+ hass.entities,
+ "humidity"
+ )}
+
${stateObj.attributes.humidity} %
@@ -182,7 +199,13 @@ class MoreInfoClimate extends LitElement {
.map(
(mode) => html`
- ${hass.localize(`component.climate.state._.${mode}`)}
+ ${computeStateDisplay(
+ hass.localize,
+ stateObj,
+ hass.locale,
+ hass.entities,
+ mode
+ )}
`
)}
@@ -194,7 +217,12 @@ class MoreInfoClimate extends LitElement {
? html`
html`
- ${hass.localize(
- `state_attributes.climate.preset_mode.${mode}`
- ) || mode}
+ ${computeAttributeValueDisplay(
+ hass.localize,
+ stateObj,
+ hass.entities,
+ "preset_mode",
+ mode
+ )}
`
)}
@@ -218,7 +250,12 @@ class MoreInfoClimate extends LitElement {
? html`
html`
- ${hass.localize(
- `state_attributes.climate.fan_mode.${mode}`
- ) || mode}
+ ${computeAttributeValueDisplay(
+ hass.localize,
+ stateObj,
+ hass.entities,
+ "fan_mode",
+ mode
+ )}
`
)}
@@ -242,7 +283,12 @@ class MoreInfoClimate extends LitElement {
? html`
${stateObj.attributes.swing_modes!.map(
(mode) => html`
- ${mode}
+
+ ${computeAttributeValueDisplay(
+ hass.localize,
+ stateObj,
+ hass.entities,
+ "swing_mode",
+ mode
+ )}
+
`
)}
@@ -263,7 +317,12 @@ class MoreInfoClimate extends LitElement {
- ${hass.localize("ui.card.climate.aux_heat")}
+ ${computeAttributeNameDisplay(
+ hass.localize,
+ stateObj,
+ hass.entities,
+ "aux_heat"
+ )}
${
stateObj.attributes.hvac_action
- ? this.hass!.localize(
- `state_attributes.climate.hvac_action.${stateObj.attributes.hvac_action}`
+ ? computeAttributeValueDisplay(
+ this.hass.localize,
+ stateObj,
+ this.hass.entities,
+ "hvac_action"
)
- : this.hass!.localize(
- `component.climate.state._.${stateObj.state}`
+ : computeStateDisplay(
+ this.hass.localize,
+ stateObj,
+ this.hass.locale,
+ this.hass.entities
)
}
${
@@ -225,9 +233,12 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
? html`
-
- ${this.hass!.localize(
- `state_attributes.climate.preset_mode.${stateObj.attributes.preset_mode}`
- ) || stateObj.attributes.preset_mode}
+ ${computeAttributeValueDisplay(
+ this.hass.localize,
+ stateObj,
+ this.hass.entities,
+ "preset_mode"
+ )}
`
: ""
}
diff --git a/src/translations/en.json b/src/translations/en.json
index b8bebcaf5f..f8415ad180 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -22,31 +22,6 @@
}
},
"state_attributes": {
- "climate": {
- "fan_mode": {
- "off": "Off",
- "on": "On",
- "auto": "Auto"
- },
- "preset_mode": {
- "none": "None",
- "eco": "Eco",
- "away": "Away",
- "boost": "Boost",
- "comfort": "Comfort",
- "home": "Home",
- "sleep": "Sleep",
- "activity": "Activity"
- },
- "hvac_action": {
- "off": "Off",
- "heating": "Heating",
- "cooling": "Cooling",
- "drying": "Drying",
- "idle": "Idle",
- "fan": "Fan"
- }
- },
"humidifier": {
"mode": {
"normal": "Normal",
@@ -140,7 +115,6 @@
"climate": {
"currently": "Currently",
"on_off": "On / off",
- "target_temperature": "Target temperature",
"target_temperature_entity": "{name} target temperature",
"target_temperature_mode": "{name} target temperature {mode}",
"current_temperature": "{name} current temperature",
@@ -148,13 +122,8 @@
"cooling": "{name} cooling",
"high": "high",
"low": "low",
- "target_humidity": "Target humidity",
"operation": "Operation",
- "fan_mode": "Fan mode",
- "swing_mode": "Swing mode",
- "preset_mode": "Preset",
- "away_mode": "Away mode",
- "aux_heat": "Aux heat"
+ "away_mode": "Away mode"
},
"counter": {
"actions": {