diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index c96c02055a..8a597fd56a 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -21,6 +21,7 @@ import { numberFormatToLocale, } from "../../common/number/format_number"; import { + getDisplayUnit, getStatisticLabel, getStatisticMetadata, Statistics, @@ -258,8 +259,11 @@ class StatisticsChart extends LitElement { if (!this.unit) { if (unit === undefined) { - unit = meta?.state_unit_of_measurement; - } else if (unit !== meta?.state_unit_of_measurement) { + unit = getDisplayUnit(this.hass, firstStat.statistic_id, meta); + } else if ( + unit !== getDisplayUnit(this.hass, firstStat.statistic_id, meta) + ) { + // Clear unit if not all statistics have same unit unit = null; } } diff --git a/src/components/entity/ha-statistic-picker.ts b/src/components/entity/ha-statistic-picker.ts index 84e071f87f..0b710f68ee 100644 --- a/src/components/entity/ha-statistic-picker.ts +++ b/src/components/entity/ha-statistic-picker.ts @@ -5,9 +5,12 @@ import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { ensureArray } from "../../common/ensure-array"; import { fireEvent } from "../../common/dom/fire_event"; -import { computeStateName } from "../../common/entity/compute_state_name"; import { stringCompare } from "../../common/string/compare"; -import { getStatisticIds, StatisticsMetaData } from "../../data/recorder"; +import { + getStatisticIds, + getStatisticLabel, + StatisticsMetaData, +} from "../../data/recorder"; import { PolymerChangedEvent } from "../../polymer-types"; import { HomeAssistant } from "../../types"; import { documentationUrl } from "../../util/documentation-url"; @@ -43,18 +46,11 @@ export class HaStatisticPicker extends LitElement { public includeStatisticsUnitOfMeasurement?: string | string[]; /** - * Show only statistics displayed with these units of measurements. - * @attr include-display-unit-of-measurement + * Show only statistics with these unit classes. + * @attr include-unit-class */ - @property({ attribute: "include-display-unit-of-measurement" }) - public includeDisplayUnitOfMeasurement?: string | string[]; - - /** - * Show only statistics with these device classes. - * @attr include-device-classes - */ - @property({ attribute: "include-device-classes" }) - public includeDeviceClasses?: string[]; + @property({ attribute: "include-unit-class" }) + public includeUnitClass?: string | string[]; /** * Show only statistics on entities. @@ -97,8 +93,7 @@ export class HaStatisticPicker extends LitElement { ( statisticIds: StatisticsMetaData[], includeStatisticsUnitOfMeasurement?: string | string[], - includeDisplayUnitOfMeasurement?: string | string[], - includeDeviceClasses?: string[], + includeUnitClass?: string | string[], entitiesOnly?: boolean ): Array<{ id: string; name: string; state?: HassEntity }> => { if (!statisticIds.length) { @@ -113,15 +108,18 @@ export class HaStatisticPicker extends LitElement { } if (includeStatisticsUnitOfMeasurement) { - const includeUnits = ensureArray(includeStatisticsUnitOfMeasurement); + const includeUnits: (string | null)[] = ensureArray( + includeStatisticsUnitOfMeasurement + ); statisticIds = statisticIds.filter((meta) => includeUnits.includes(meta.statistics_unit_of_measurement) ); } - if (includeDisplayUnitOfMeasurement) { - const includeUnits = ensureArray(includeDisplayUnitOfMeasurement); + if (includeUnitClass) { + const includeUnitClasses: (string | null)[] = + ensureArray(includeUnitClass); statisticIds = statisticIds.filter((meta) => - includeUnits.includes(meta.state_unit_of_measurement) + includeUnitClasses.includes(meta.unit_class) ); } @@ -136,23 +134,16 @@ export class HaStatisticPicker extends LitElement { if (!entitiesOnly) { output.push({ id: meta.statistic_id, - name: meta.name || meta.statistic_id, + name: getStatisticLabel(this.hass, meta.statistic_id, meta), }); } return; } - if ( - !includeDeviceClasses || - includeDeviceClasses.includes( - entityState!.attributes.device_class || "" - ) - ) { - output.push({ - id: meta.statistic_id, - name: computeStateName(entityState), - state: entityState, - }); - } + output.push({ + id: meta.statistic_id, + name: getStatisticLabel(this.hass, meta.statistic_id, meta), + state: entityState, + }); }); if (!output.length) { @@ -203,8 +194,7 @@ export class HaStatisticPicker extends LitElement { (this.comboBox as any).items = this._getStatistics( this.statisticIds!, this.includeStatisticsUnitOfMeasurement, - this.includeDisplayUnitOfMeasurement, - this.includeDeviceClasses, + this.includeUnitClass, this.entitiesOnly ); } else { @@ -212,8 +202,7 @@ export class HaStatisticPicker extends LitElement { (this.comboBox as any).items = this._getStatistics( this.statisticIds!, this.includeStatisticsUnitOfMeasurement, - this.includeDisplayUnitOfMeasurement, - this.includeDeviceClasses, + this.includeUnitClass, this.entitiesOnly ); }); diff --git a/src/components/entity/ha-statistics-picker.ts b/src/components/entity/ha-statistics-picker.ts index ef450d4ae2..62d7c0d2b7 100644 --- a/src/components/entity/ha-statistics-picker.ts +++ b/src/components/entity/ha-statistics-picker.ts @@ -32,11 +32,11 @@ class HaStatisticsPicker extends LitElement { public includeStatisticsUnitOfMeasurement?: string[] | string; /** - * Show only statistics displayed with these units of measurements. - * @attr include-display-unit-of-measurement + * Show only statistics with these unit classes. + * @attr include-unit-class */ - @property({ attribute: "include-display-unit-of-measurement" }) - public includeDisplayUnitOfMeasurement?: string[] | string; + @property({ attribute: "include-unit-class" }) + public includeUnitClass?: string | string[]; /** * Ignore filtering of statistics type and units when only a single statistic is selected. @@ -58,12 +58,12 @@ class HaStatisticsPicker extends LitElement { this.ignoreRestrictionsOnFirstStatistic && this._currentStatistics.length <= 1; - const includeDisplayUnitCurrent = ignoreRestriction - ? undefined - : this.includeDisplayUnitOfMeasurement; const includeStatisticsUnitCurrent = ignoreRestriction ? undefined : this.includeStatisticsUnitOfMeasurement; + const includeUnitClassCurrent = ignoreRestriction + ? undefined + : this.includeUnitClass; const includeStatisticTypesCurrent = ignoreRestriction ? undefined : this.statisticTypes; @@ -75,8 +75,8 @@ class HaStatisticsPicker extends LitElement { type: "energy/solar_forecast", }); -export const ENERGY_GAS_VOLUME_UNITS = ["m³"]; -export const ENERGY_GAS_ENERGY_UNITS = ["kWh"]; -export const ENERGY_GAS_UNITS = [ - ...ENERGY_GAS_VOLUME_UNITS, - ...ENERGY_GAS_ENERGY_UNITS, -]; +const energyGasUnitClass = ["volume", "energy"] as const; +export type EnergyGasUnitClass = typeof energyGasUnitClass[number]; -export type EnergyGasUnit = "volume" | "energy"; - -export const getEnergyGasUnitCategory = ( +export const getEnergyGasUnitClass = ( prefs: EnergyPreferences, statisticsMetaData: Record = {}, excludeSource?: string -): EnergyGasUnit | undefined => { +): EnergyGasUnitClass | undefined => { for (const source of prefs.energy_sources) { if (source.type !== "gas") { continue; @@ -622,29 +616,29 @@ export const getEnergyGasUnitCategory = ( continue; } const statisticIdWithMeta = statisticsMetaData[source.stat_energy_from]; - if (statisticIdWithMeta) { - return ENERGY_GAS_VOLUME_UNITS.includes( - statisticIdWithMeta.statistics_unit_of_measurement + if ( + energyGasUnitClass.includes( + statisticIdWithMeta.unit_class as EnergyGasUnitClass ) - ? "volume" - : "energy"; + ) { + return statisticIdWithMeta.unit_class as EnergyGasUnitClass; } } return undefined; }; export const getEnergyGasUnit = ( + hass: HomeAssistant, prefs: EnergyPreferences, statisticsMetaData: Record = {} ): string | undefined => { - for (const source of prefs.energy_sources) { - if (source.type !== "gas") { - continue; - } - const statisticIdWithMeta = statisticsMetaData[source.stat_energy_from]; - if (statisticIdWithMeta?.state_unit_of_measurement) { - return statisticIdWithMeta.state_unit_of_measurement; - } + const unitClass = getEnergyGasUnitClass(prefs, statisticsMetaData); + if (unitClass === undefined) { + return undefined; } - return undefined; + return unitClass === "energy" + ? "kWh" + : hass.config.unit_system.length === "km" + ? "m³" + : "ft³"; }; diff --git a/src/data/recorder.ts b/src/data/recorder.ts index 578b709e12..5d6fd71806 100644 --- a/src/data/recorder.ts +++ b/src/data/recorder.ts @@ -20,13 +20,13 @@ export interface StatisticValue { } export interface StatisticsMetaData { - state_unit_of_measurement: string; - statistics_unit_of_measurement: string; + statistics_unit_of_measurement: string | null; statistic_id: string; source: string; name?: string | null; has_sum: boolean; has_mean: boolean; + unit_class: string | null; } export type StatisticsValidationResult = @@ -254,7 +254,7 @@ export const adjustStatisticsSum = ( statistic_id: string, start_time: string, adjustment: number, - adjustment_unit_of_measurement: string + adjustment_unit_of_measurement: string | null ): Promise => hass.callWS({ type: "recorder/adjust_sum_statistics", @@ -275,3 +275,17 @@ export const getStatisticLabel = ( } return statisticsMetaData?.name || statisticsId; }; + +export const getDisplayUnit = ( + hass: HomeAssistant, + statisticsId: string | undefined, + statisticsMetaData: StatisticsMetaData | undefined +): string | null | undefined => { + let unit: string | undefined; + if (statisticsId) { + unit = hass.states[statisticsId]?.attributes.unit_of_measurement; + } + return unit === undefined + ? statisticsMetaData?.statistics_unit_of_measurement + : unit; +}; diff --git a/src/panels/config/energy/components/ha-energy-gas-settings.ts b/src/panels/config/energy/components/ha-energy-gas-settings.ts index c50bc87de1..14f6653f05 100644 --- a/src/panels/config/energy/components/ha-energy-gas-settings.ts +++ b/src/panels/config/energy/components/ha-energy-gas-settings.ts @@ -10,7 +10,7 @@ import { EnergyPreferencesValidation, EnergyValidationIssue, GasSourceTypeEnergyPreference, - getEnergyGasUnitCategory, + getEnergyGasUnitClass, saveEnergyPreferences, } from "../../../../data/energy"; import { @@ -133,7 +133,7 @@ export class EnergyGasSettings extends LitElement { private _addSource() { showEnergySettingsGasDialog(this, { - allowedGasUnitCategory: getEnergyGasUnitCategory( + allowedGasUnitClass: getEnergyGasUnitClass( this.preferences, this.statsMetadata ), @@ -152,7 +152,7 @@ export class EnergyGasSettings extends LitElement { ev.currentTarget.closest(".row").source; showEnergySettingsGasDialog(this, { source: { ...origSource }, - allowedGasUnitCategory: getEnergyGasUnitCategory( + allowedGasUnitClass: getEnergyGasUnitClass( this.preferences, this.statsMetadata, origSource.stat_energy_from diff --git a/src/panels/config/energy/dialogs/dialog-energy-battery-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-battery-settings.ts index 0314aefc0d..2dd5ce4e15 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-battery-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-battery-settings.ts @@ -14,8 +14,7 @@ import { EnergySettingsBatteryDialogParams } from "./show-dialogs-energy"; import "@material/mwc-button/mwc-button"; import "../../../../components/entity/ha-statistic-picker"; -const energyUnits = ["kWh"]; -const energyDeviceClasses = ["energy"]; +const energyUnitClasses = ["energy"]; @customElement("dialog-energy-battery-settings") export class DialogEnergyBatterySettings @@ -67,8 +66,7 @@ export class DialogEnergyBatterySettings ) { if (ev.detail.value) { - const entity = this.hass.states[ev.detail.value]; - if (entity?.attributes.unit_of_measurement) { - this._pickedDisplayUnit = entity.attributes.unit_of_measurement; - } else { - this._pickedDisplayUnit = ( - await getStatisticMetadata(this.hass, [ev.detail.value]) - )[0]?.state_unit_of_measurement; - } + const metadata = await getStatisticMetadata(this.hass, [ev.detail.value]); + this._pickedDisplayUnit = getDisplayUnit( + this.hass, + ev.detail.value, + metadata[0] + ); } else { this._pickedDisplayUnit = undefined; } diff --git a/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts index c2cab0d7b2..68e9879bf8 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts @@ -20,8 +20,7 @@ import "../../../../components/ha-formfield"; import type { HaRadio } from "../../../../components/ha-radio"; import "../../../../components/entity/ha-entity-picker"; -const energyUnits = ["kWh"]; -const energyDeviceClasses = ["energy"]; +const energyUnitClasses = ["energy"]; @customElement("dialog-energy-grid-flow-settings") export class DialogEnergyGridFlowSettings @@ -93,8 +92,7 @@ export class DialogEnergyGridFlowSettings Promise; } diff --git a/src/panels/developer-tools/statistics/developer-tools-statistics.ts b/src/panels/developer-tools/statistics/developer-tools-statistics.ts index d483fc84ce..7cd162ccfe 100644 --- a/src/panels/developer-tools/statistics/developer-tools-statistics.ts +++ b/src/panels/developer-tools/statistics/developer-tools-statistics.ts @@ -74,12 +74,6 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) { hidden: this.narrow, width: "20%", }, - state_unit_of_measurement: { - title: "Display unit", - sortable: true, - filterable: true, - width: "10%", - }, statistics_unit_of_measurement: { title: "Statistics unit", sortable: true, @@ -220,12 +214,12 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) { this._data.push({ statistic_id: statisticId, statistics_unit_of_measurement: "", - state_unit_of_measurement: "", source: "", state: this.hass.states[statisticId], issues: issues[statisticId], has_mean: false, has_sum: false, + unit_class: null, }); } }); diff --git a/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts b/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts index c00b5dc5fd..4c6c2998ad 100644 --- a/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts +++ b/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts @@ -23,6 +23,7 @@ import "../../../components/ha-svg-icon"; import { adjustStatisticsSum, fetchStatistics, + getDisplayUnit, StatisticValue, } from "../../../data/recorder"; import type { DateTimeSelector, NumberSelector } from "../../../data/selector"; @@ -59,7 +60,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { }; private _amountSelector = memoizeOne( - (unit_of_measurement: string): NumberSelector => ({ + (unit_of_measurement: string | undefined): NumberSelector => ({ number: { step: 0.01, unit_of_measurement, @@ -135,7 +136,11 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { } else { const data = this._stats5min.length >= 2 ? this._stats5min : this._statsHour; - const unit = this._params!.statistic.state_unit_of_measurement; + const unit = getDisplayUnit( + this.hass, + this._params!.statistic.statistic_id, + this._params!.statistic + ); const rows: TemplateResult[] = []; for (let i = 1; i < data.length; i++) { const stat = data[i]; @@ -192,6 +197,11 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { } private _renderAdjustStat() { + const unit = getDisplayUnit( + this.hass, + this._params!.statistic.statistic_id, + this._params!.statistic + ); return html`
Statistic: ${this._params!.statistic.statistic_id} @@ -220,9 +230,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { { @@ -299,6 +307,11 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { } private async _fixIssue(): Promise { + const unit = getDisplayUnit( + this.hass, + this._params!.statistic.statistic_id, + this._params!.statistic + ); this._busy = true; try { await adjustStatisticsSum( @@ -306,7 +319,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { this._params!.statistic.statistic_id, this._chosenStat!.start, this._amount! - this._origAmount!, - this._params!.statistic.state_unit_of_measurement + unit || null ); } catch (err: any) { this._busy = false; diff --git a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts index d204562559..cb311b7637 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts @@ -315,8 +315,11 @@ class HuiEnergyDistrubutionCard ${formatNumber(gasUsage || 0, this.hass.locale, { maximumFractionDigits: 1, })} - ${getEnergyGasUnit(prefs, this._data.statsMetadata) || - "m³"} + ${getEnergyGasUnit( + this.hass, + prefs, + this._data.statsMetadata + ) || "m³"}
diff --git a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts index fc940d5605..2bd6855782 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts @@ -274,7 +274,8 @@ export class HuiEnergyGasGraphCard ) as GasSourceTypeEnergyPreference[]; this._unit = - getEnergyGasUnit(energyData.prefs, energyData.statsMetadata) || "m³"; + getEnergyGasUnit(this.hass, energyData.prefs, energyData.statsMetadata) || + "m³"; const datasets: ChartDataset<"bar", ScatterDataPoint[]>[] = []; diff --git a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts index dc5eb4b977..3880736f53 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts @@ -130,7 +130,8 @@ export class HuiEnergySourcesTableCard ); const gasUnit = - getEnergyGasUnit(this._data.prefs, this._data.statsMetadata) || ""; + getEnergyGasUnit(this.hass, this._data.prefs, this._data.statsMetadata) || + ""; const compare = this._data.statsCompare !== undefined; diff --git a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts index 8933560cd4..e894f09411 100644 --- a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts @@ -205,7 +205,7 @@ export class HuiStatisticsGraphCardEditor ...this._config, stat_types: configured_stat_types, }; - const displayUnit = this._metaDatas?.[0]?.state_unit_of_measurement; + const unitClass = this._metaDatas?.[0]?.unit_class; return html`