Uses backend translation for climate attributes (#14827)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Paul Bottein 2022-12-27 21:08:47 +01:00 committed by GitHub
parent c6aa2886ed
commit 0e70b866ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 199 additions and 92 deletions

View File

@ -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
);
};

View File

@ -1,5 +1,7 @@
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 { computeAttributeValueDisplay } from "../common/entity/compute_attribute_display";
import { computeStateDisplay } from "../common/entity/compute_state_display";
import { formatNumber } from "../common/number/format_number"; import { formatNumber } from "../common/number/format_number";
import { ClimateEntity, CLIMATE_PRESET_NONE } from "../data/climate"; import { ClimateEntity, CLIMATE_PRESET_NONE } from "../data/climate";
import { isUnavailableState } from "../data/entity"; import { isUnavailableState } from "../data/entity";
@ -21,9 +23,12 @@ class HaClimateState extends LitElement {
${this.stateObj.attributes.preset_mode && ${this.stateObj.attributes.preset_mode &&
this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
? html`- ? html`-
${this.hass.localize( ${computeAttributeValueDisplay(
`state_attributes.climate.preset_mode.${this.stateObj.attributes.preset_mode}` this.hass.localize,
) || this.stateObj.attributes.preset_mode}` this.stateObj,
this.hass.entities,
"preset_mode"
)}`
: ""} : ""}
</span> </span>
<div class="unit">${this._computeTarget()}</div>` <div class="unit">${this._computeTarget()}</div>`
@ -112,13 +117,19 @@ class HaClimateState extends LitElement {
return this.hass.localize(`state.default.${this.stateObj.state}`); return this.hass.localize(`state.default.${this.stateObj.state}`);
} }
const stateString = this.hass.localize( const stateString = computeStateDisplay(
`component.climate.state._.${this.stateObj.state}` this.hass.localize,
this.stateObj,
this.hass.locale,
this.hass.entities
); );
return this.stateObj.attributes.hvac_action return this.stateObj.attributes.hvac_action
? `${this.hass.localize( ? `${computeAttributeValueDisplay(
`state_attributes.climate.hvac_action.${this.stateObj.attributes.hvac_action}` this.hass.localize,
this.stateObj,
this.hass.entities,
"hvac_action"
)} (${stateString})` )} (${stateString})`
: stateString; : stateString;
} }

View File

@ -2,7 +2,6 @@ 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"
@ -15,12 +14,13 @@ export type HvacMode =
export const CLIMATE_PRESET_NONE = "none"; export const CLIMATE_PRESET_NONE = "none";
type ClimateAttributes = TranslationDict["state_attributes"]["climate"]; export type HvacAction =
export type HvacAction = keyof ClimateAttributes["hvac_action"]; | "off"
export type FanMode = keyof ClimateAttributes["fan_mode"]; | "heating"
export type PresetMode = | "cooling"
| keyof ClimateAttributes["preset_mode"] | "drying"
| typeof CLIMATE_PRESET_NONE; | "idle"
| "fan";
export type ClimateEntity = HassEntityBase & { export type ClimateEntity = HassEntityBase & {
attributes: HassEntityAttributeBase & { attributes: HassEntityAttributeBase & {
@ -40,23 +40,25 @@ 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?: FanMode; fan_mode?: string;
fan_modes?: FanMode[]; fan_modes?: string[];
preset_mode?: PresetMode; preset_mode?: string;
preset_modes?: PresetMode[]; preset_modes?: string[];
swing_mode?: string; swing_mode?: string;
swing_modes?: string[]; swing_modes?: string[];
aux_heat?: "on" | "off"; aux_heat?: "on" | "off";
}; };
}; };
export const CLIMATE_SUPPORT_TARGET_TEMPERATURE = 1; export const enum ClimateEntityFeature {
export const CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE = 2; TARGET_TEMPERATURE = 1,
export const CLIMATE_SUPPORT_TARGET_HUMIDITY = 4; TARGET_TEMPERATURE_RANGE = 2,
export const CLIMATE_SUPPORT_FAN_MODE = 8; TARGET_HUMIDITY = 4,
export const CLIMATE_SUPPORT_PRESET_MODE = 16; FAN_MODE = 8,
export const CLIMATE_SUPPORT_SWING_MODE = 32; PRESET_MODE = 16,
export const CLIMATE_SUPPORT_AUX_HEAT = 64; SWING_MODE = 32,
AUX_HEAT = 64,
}
const hvacModeOrdering: { [key in HvacMode]: number } = { const hvacModeOrdering: { [key in HvacMode]: number } = {
auto: 1, auto: 1,

View File

@ -44,6 +44,7 @@ declare global {
export type TranslationCategory = export type TranslationCategory =
| "title" | "title"
| "state" | "state"
| "state_attributes"
| "entity" | "entity"
| "config" | "config"
| "config_panel" | "config_panel"

View File

@ -11,6 +11,11 @@ import { property } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { stopPropagation } from "../../../common/dom/stop_propagation"; 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 { supportsFeature } from "../../../common/entity/supports-feature";
import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-climate-control"; import "../../../components/ha-climate-control";
@ -19,13 +24,7 @@ import "../../../components/ha-slider";
import "../../../components/ha-switch"; import "../../../components/ha-switch";
import { import {
ClimateEntity, ClimateEntity,
CLIMATE_SUPPORT_AUX_HEAT, ClimateEntityFeature,
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,
compareClimateHvacModes, compareClimateHvacModes,
} from "../../../data/climate"; } from "../../../data/climate";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
@ -47,26 +46,32 @@ class MoreInfoClimate extends LitElement {
const supportTargetTemperature = supportsFeature( const supportTargetTemperature = supportsFeature(
stateObj, stateObj,
CLIMATE_SUPPORT_TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
); );
const supportTargetTemperatureRange = supportsFeature( const supportTargetTemperatureRange = supportsFeature(
stateObj, stateObj,
CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
); );
const supportTargetHumidity = supportsFeature( const supportTargetHumidity = supportsFeature(
stateObj, 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( const supportPresetMode = supportsFeature(
stateObj, stateObj,
CLIMATE_SUPPORT_PRESET_MODE ClimateEntityFeature.PRESET_MODE
); );
const supportSwingMode = supportsFeature( const supportSwingMode = supportsFeature(
stateObj, 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 = const temperatureStepSize =
stateObj.attributes.target_temp_step || stateObj.attributes.target_temp_step ||
@ -94,7 +99,12 @@ class MoreInfoClimate extends LitElement {
${supportTargetTemperature || supportTargetTemperatureRange ${supportTargetTemperature || supportTargetTemperatureRange
? html` ? html`
<div> <div>
${hass.localize("ui.card.climate.target_temperature")} ${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"temperature"
)}
</div> </div>
` `
: ""} : ""}
@ -145,7 +155,14 @@ class MoreInfoClimate extends LitElement {
${supportTargetHumidity ${supportTargetHumidity
? html` ? html`
<div class="container-humidity"> <div class="container-humidity">
<div>${hass.localize("ui.card.climate.target_humidity")}</div> <div>
${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"humidity"
)}
</div>
<div class="single-row"> <div class="single-row">
<div class="target-humidity"> <div class="target-humidity">
${stateObj.attributes.humidity} % ${stateObj.attributes.humidity} %
@ -182,7 +199,13 @@ class MoreInfoClimate extends LitElement {
.map( .map(
(mode) => html` (mode) => html`
<mwc-list-item .value=${mode}> <mwc-list-item .value=${mode}>
${hass.localize(`component.climate.state._.${mode}`)} ${computeStateDisplay(
hass.localize,
stateObj,
hass.locale,
hass.entities,
mode
)}
</mwc-list-item> </mwc-list-item>
` `
)} )}
@ -194,7 +217,12 @@ class MoreInfoClimate extends LitElement {
? html` ? html`
<div class="container-preset_modes"> <div class="container-preset_modes">
<ha-select <ha-select
.label=${hass.localize("ui.card.climate.preset_mode")} .label=${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"preset_mode"
)}
.value=${stateObj.attributes.preset_mode} .value=${stateObj.attributes.preset_mode}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@ -204,9 +232,13 @@ class MoreInfoClimate extends LitElement {
${stateObj.attributes.preset_modes!.map( ${stateObj.attributes.preset_modes!.map(
(mode) => html` (mode) => html`
<mwc-list-item .value=${mode}> <mwc-list-item .value=${mode}>
${hass.localize( ${computeAttributeValueDisplay(
`state_attributes.climate.preset_mode.${mode}` hass.localize,
) || mode} stateObj,
hass.entities,
"preset_mode",
mode
)}
</mwc-list-item> </mwc-list-item>
` `
)} )}
@ -218,7 +250,12 @@ class MoreInfoClimate extends LitElement {
? html` ? html`
<div class="container-fan_list"> <div class="container-fan_list">
<ha-select <ha-select
.label=${hass.localize("ui.card.climate.fan_mode")} .label=${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"fan_mode"
)}
.value=${stateObj.attributes.fan_mode} .value=${stateObj.attributes.fan_mode}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@ -228,9 +265,13 @@ class MoreInfoClimate extends LitElement {
${stateObj.attributes.fan_modes!.map( ${stateObj.attributes.fan_modes!.map(
(mode) => html` (mode) => html`
<mwc-list-item .value=${mode}> <mwc-list-item .value=${mode}>
${hass.localize( ${computeAttributeValueDisplay(
`state_attributes.climate.fan_mode.${mode}` hass.localize,
) || mode} stateObj,
hass.entities,
"fan_mode",
mode
)}
</mwc-list-item> </mwc-list-item>
` `
)} )}
@ -242,7 +283,12 @@ class MoreInfoClimate extends LitElement {
? html` ? html`
<div class="container-swing_list"> <div class="container-swing_list">
<ha-select <ha-select
.label=${hass.localize("ui.card.climate.swing_mode")} .label=${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"swing_mode"
)}
.value=${stateObj.attributes.swing_mode} .value=${stateObj.attributes.swing_mode}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@ -251,7 +297,15 @@ class MoreInfoClimate extends LitElement {
> >
${stateObj.attributes.swing_modes!.map( ${stateObj.attributes.swing_modes!.map(
(mode) => html` (mode) => html`
<mwc-list-item .value=${mode}>${mode}</mwc-list-item> <mwc-list-item .value=${mode}>
${computeAttributeValueDisplay(
hass.localize,
stateObj,
hass.entities,
"swing_mode",
mode
)}
</mwc-list-item>
` `
)} )}
</ha-select> </ha-select>
@ -263,7 +317,12 @@ class MoreInfoClimate extends LitElement {
<div class="container-aux_heat"> <div class="container-aux_heat">
<div class="center horizontal layout single-row"> <div class="center horizontal layout single-row">
<div class="flex"> <div class="flex">
${hass.localize("ui.card.climate.aux_heat")} ${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"aux_heat"
)}
</div> </div>
<ha-switch <ha-switch
.checked=${stateObj.attributes.aux_heat === "on"} .checked=${stateObj.attributes.aux_heat === "on"}

View File

@ -138,6 +138,8 @@ export class HomeAssistantAppEl extends QuickBarMixin(HassElement) {
// @ts-ignore // @ts-ignore
this._loadHassTranslations(this.hass!.language, "state"); this._loadHassTranslations(this.hass!.language, "state");
// @ts-ignore // @ts-ignore
this._loadHassTranslations(this.hass!.language, "state_attributes");
// @ts-ignore
this._loadHassTranslations(this.hass!.language, "entity"); this._loadHassTranslations(this.hass!.language, "entity");
document.addEventListener( document.addEventListener(

View File

@ -24,6 +24,8 @@ import { classMap } from "lit/directives/class-map";
import { UNIT_F } from "../../../common/const"; import { UNIT_F } from "../../../common/const";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display";
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
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/ha-card"; import "../../../components/ha-card";
@ -213,11 +215,17 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
> >
${ ${
stateObj.attributes.hvac_action stateObj.attributes.hvac_action
? this.hass!.localize( ? computeAttributeValueDisplay(
`state_attributes.climate.hvac_action.${stateObj.attributes.hvac_action}` this.hass.localize,
stateObj,
this.hass.entities,
"hvac_action"
) )
: this.hass!.localize( : computeStateDisplay(
`component.climate.state._.${stateObj.state}` 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 stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
? html` ? html`
- -
${this.hass!.localize( ${computeAttributeValueDisplay(
`state_attributes.climate.preset_mode.${stateObj.attributes.preset_mode}` this.hass.localize,
) || stateObj.attributes.preset_mode} stateObj,
this.hass.entities,
"preset_mode"
)}
` `
: "" : ""
} }

View File

@ -22,31 +22,6 @@
} }
}, },
"state_attributes": { "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": { "humidifier": {
"mode": { "mode": {
"normal": "Normal", "normal": "Normal",
@ -140,7 +115,6 @@
"climate": { "climate": {
"currently": "Currently", "currently": "Currently",
"on_off": "On / off", "on_off": "On / off",
"target_temperature": "Target temperature",
"target_temperature_entity": "{name} target temperature", "target_temperature_entity": "{name} target temperature",
"target_temperature_mode": "{name} target temperature {mode}", "target_temperature_mode": "{name} target temperature {mode}",
"current_temperature": "{name} current temperature", "current_temperature": "{name} current temperature",
@ -148,13 +122,8 @@
"cooling": "{name} cooling", "cooling": "{name} cooling",
"high": "high", "high": "high",
"low": "low", "low": "low",
"target_humidity": "Target humidity",
"operation": "Operation", "operation": "Operation",
"fan_mode": "Fan mode", "away_mode": "Away mode"
"swing_mode": "Swing mode",
"preset_mode": "Preset",
"away_mode": "Away mode",
"aux_heat": "Aux heat"
}, },
"counter": { "counter": {
"actions": { "actions": {