From 72d7c4133ff96713ecbe42168db0d6732b82510c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Wiedemann?= Date: Mon, 24 Jul 2023 23:37:49 +0000 Subject: [PATCH] fix: numerical states would not follow HA's format Fix #662 --- README.md | 8 ++++++- src/button-card.ts | 37 ++++++++++------------------- src/common/compute_state_display.ts | 30 +++++++++++++---------- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 2844b7b..0d6c2ec 100644 --- a/README.md +++ b/README.md @@ -334,7 +334,13 @@ Inside the javascript code, you'll have access to those variables: - `user`: The user object (equivalent to `hass.user`) - `hass`: The complete `hass` object - `variables`: an object containing all your variables defined in the configuration. See [Variables](#variables) -- `localize(entity, state?)`: a function which localizes a state (eg. `localize(entity)`) and returns a string. Takes an entity object as argument (not the state of the entity as we need context) and takes an optional `state` string as argument. If `state` is not provided, it localizes the state of the `entity` (Eg. `localize(entity)` or `localize(states['weather.your_city'])`). If `state` is provided, it localizes `state` in the context of the `entity` (eg. : `localize(states['weather.your_city'], states['weather.your_city'].attributes.forecast[0].condition)`) +- `localize(entity, state?, numeric_precision?, show_units?, units?)`: a function which localizes a state (eg. `localize(entity)`) and returns a string. Takes an entity object as argument (not the state of the entity as we need context) and takes optional arguments. + - If `state` is not provided, it localizes the state of the `entity` (Eg. `localize(entity)` or `localize(states['weather.your_city'])`). + - If `state` is provided, it localizes `state` in the context of the `entity` (eg. : `localize(entity, entity.attributes.forecast[0].condition)` or `localize(states['weather.your_city'], states['weather.your_city'].attributes.forecast[0].condition)`) + - `numeric_precision` (number): For state which are numbers, force the precision instead of letting HA decide for you + - `show_units` (boolean): Will display units or not. Default is to display them. + - `units` (string): Will force the units to be the value of that parameter. + - To skip one or multiple parameter while calling the function, use `undefined`. Eg. `localize(states['sensor.temperature'], undefined, 1, undefined, 'Celcius')` - `formatDateTime(date)`, `formatShortDateTimeWithYear(date)`, `formatShortDateTime(date)`, `formatDateTimeWithSeconds(date)`, `formatDateTimeNumeric(date)`: Some helper functions to format a date time string or Date object. Name are pretty explicit. Example: `return formatDateTime(entity.attribute.last_changed)` See [here](#templates-support) for some examples or [here](#custom-fields) for some crazy advanced stuff using templates! diff --git a/src/button-card.ts b/src/button-card.ts index 93a0d58..a1b7389 100644 --- a/src/button-card.ts +++ b/src/button-card.ts @@ -294,7 +294,13 @@ class ButtonCard extends LitElement { return retval; } - private _localize(stateObj: HassEntity, state?: string): string { + private _localize( + stateObj: HassEntity, + state?: string, + numeric_precision?: number, + show_units = true, + units?: string, + ): string { // eslint-disable-next-line @typescript-eslint/no-this-alias return computeStateDisplay( this._hass!.localize, @@ -302,7 +308,7 @@ class ButtonCard extends LitElement { this._hass!.locale, this._hass!.config, this._hass!.entities, - this._config?.numeric_precision, + { numeric_precision: numeric_precision || this._config?.numeric_precision, show_units, units }, state, ); } @@ -543,10 +549,7 @@ class ButtonCard extends LitElement { private _buildStateString(stateObj: HassEntity | undefined): string | undefined | null { let stateString: string | undefined | null; if (this._config!.show_state && stateObj && stateObj.state) { - const units = this._buildUnits(stateObj); - if (units) { - stateString = `${stateObj.state} ${units}`; - } else if (computeDomain(stateObj.entity_id) === 'timer') { + if (computeDomain(stateObj.entity_id) === 'timer') { if (stateObj.state === 'idle' || this._timeRemaining === 0) { stateString = computeStateDisplay( this._hass!.localize, @@ -554,7 +557,7 @@ class ButtonCard extends LitElement { this._hass!.locale, this._hass!.config, this._hass!.entities, - this._config?.numeric_precision, + this._config, ); } else { stateString = this._computeTimeDisplay(stateObj); @@ -565,12 +568,10 @@ class ButtonCard extends LitElement { this._hass!.locale, this._hass!.config, this._hass!.entities, - this._config?.numeric_precision, + this._config, )})`; } } - } else if (!this._config?.show_units && computeDomain(stateObj.entity_id) === 'sensor') { - stateString = stateObj.state; } else { stateString = computeStateDisplay( this._hass!.localize, @@ -578,27 +579,13 @@ class ButtonCard extends LitElement { this._hass!.locale, this._hass!.config, this._hass!.entities, - this._config?.numeric_precision, + this._config, ); } } return stateString; } - private _buildUnits(state: HassEntity | undefined): string | undefined { - let units: string | undefined; - if (state) { - if (this._config!.show_units) { - if (state.attributes?.unit_of_measurement && !this._config!.units) { - units = state.attributes.unit_of_measurement; - } else { - units = this._config!.units ? this._config!.units : undefined; - } - } - } - return units; - } - private _buildLastChanged(state: HassEntity | undefined, style: StyleInfo): TemplateResult | undefined { return this._config!.show_last_changed && state ? html` diff --git a/src/common/compute_state_display.ts b/src/common/compute_state_display.ts index 42233dd..1f0871b 100644 --- a/src/common/compute_state_display.ts +++ b/src/common/compute_state_display.ts @@ -14,6 +14,7 @@ import { formatDate } from './format_date'; import { formatTime } from './format_time'; import { UPDATE_SUPPORT_PROGRESS, updateIsInstallingFromAttributes } from './update'; import { supportsFeatureFromAttributes } from './supports-features'; +import { ButtonCardConfig } from '../types/types'; const UNAVAILABLE = 'unavailable'; const UNKNOWN = 'unknown'; @@ -24,7 +25,7 @@ export const computeStateDisplaySingleEntity = ( locale: FrontendLocaleData, config: HassConfig, entity: EntityRegistryDisplayEntry | undefined, - numeric_precision: number | undefined, + buttonConfig: { numeric_precision?: number; show_units?: boolean; units?: string } | undefined, state?: string, ): string => computeStateDisplayFromEntityAttributes( @@ -34,7 +35,7 @@ export const computeStateDisplaySingleEntity = ( entity, stateObj.entity_id, stateObj.attributes, - numeric_precision, + buttonConfig, state !== undefined ? state : stateObj.state, ); @@ -44,7 +45,7 @@ export const computeStateDisplay = ( locale: FrontendLocaleData, config: HassConfig, entities: HomeAssistant['entities'], - numeric_precision: number | undefined, + buttonConfig: { numeric_precision?: number; show_units?: boolean; units?: string } | undefined, state?: string, ): string => { const entity = entities[stateObj.entity_id] as EntityRegistryDisplayEntry | undefined; @@ -56,7 +57,7 @@ export const computeStateDisplay = ( entity, stateObj.entity_id, stateObj.attributes, - numeric_precision, + buttonConfig, state !== undefined ? state : stateObj.state, ); }; @@ -68,7 +69,7 @@ export const computeStateDisplayFromEntityAttributes = ( entity: EntityRegistryDisplayEntry | undefined, entityId: string, attributes: any, - numeric_precision: number | undefined, + buttonConfig: { numeric_precision?: number; show_units?: boolean; units?: string } | undefined, state: string, ): string => { if (state === UNKNOWN || state === UNAVAILABLE) { @@ -93,24 +94,29 @@ export const computeStateDisplayFromEntityAttributes = ( try { return formatNumber(state, locale, { style: 'currency', - currency: attributes.unit_of_measurement, + currency: buttonConfig?.units || attributes.unit_of_measurement, minimumFractionDigits: 2, // Override monetary options with number format - ...getNumberFormatOptions({ state, attributes } as HassEntity, numeric_precision, entity), + ...getNumberFormatOptions({ state, attributes } as HassEntity, buttonConfig?.numeric_precision, entity), }); } catch (_err) { // fallback to default } } - const unit = !attributes.unit_of_measurement + const localOrConfigUnit = buttonConfig?.show_units + ? buttonConfig?.units + ? buttonConfig?.units + : attributes.unit_of_measurement + : undefined; + const unit = !localOrConfigUnit ? '' - : attributes.unit_of_measurement === '%' + : localOrConfigUnit === '%' ? blankBeforePercent(locale) + '%' - : ` ${attributes.unit_of_measurement}`; + : ` ${localOrConfigUnit}`; return `${formatNumber( state, locale, - getNumberFormatOptions({ state, attributes } as HassEntity, numeric_precision, entity), + getNumberFormatOptions({ state, attributes } as HassEntity, buttonConfig?.numeric_precision, entity), )}${unit}`; } @@ -161,7 +167,7 @@ export const computeStateDisplayFromEntityAttributes = ( return formatNumber( state, locale, - getNumberFormatOptions({ state, attributes } as HassEntity, numeric_precision, entity), + getNumberFormatOptions({ state, attributes } as HassEntity, buttonConfig?.numeric_precision, entity), ); }