From 9f05f4df50c55dbb2a62cab708e8587916d3bdd0 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Mon, 24 Mar 2025 04:43:11 -0700 Subject: [PATCH] Short-format numbers in energy-distribution-card (#24716) --- src/data/energy.ts | 29 +++++ .../energy/hui-energy-distribution-card.ts | 104 +++++++++--------- test/data/energy.test.ts | 82 ++++++++++++++ 3 files changed, 163 insertions(+), 52 deletions(-) create mode 100644 test/data/energy.test.ts diff --git a/src/data/energy.ts b/src/data/energy.ts index b647807a9d..7c6dee5ade 100644 --- a/src/data/energy.ts +++ b/src/data/energy.ts @@ -29,6 +29,7 @@ import type { import { fetchStatistics, getStatisticMetadata } from "./recorder"; import { calcDateRange } from "../common/datetime/calc_date_range"; import type { DateRange } from "../common/datetime/calc_date_range"; +import { formatNumber } from "../common/number/format_number"; const energyCollectionKeys: (string | undefined)[] = []; @@ -924,3 +925,31 @@ const computeConsumptionDataPartial = ( return outData; }; + +export const formatConsumptionShort = ( + hass: HomeAssistant, + consumption: number | null, + unit: string +): string => { + if (!consumption) { + return `0 ${unit}`; + } + const units = ["kWh", "MWh", "GWh", "TWh"]; + let pickedUnit = unit; + let val = consumption; + let unitIndex = units.findIndex((u) => u === unit); + if (unitIndex >= 0) { + while (val >= 1000 && unitIndex < units.length - 1) { + val /= 1000; + unitIndex++; + } + pickedUnit = units[unitIndex]; + } + return ( + formatNumber(val, hass.locale, { + maximumFractionDigits: val < 10 ? 2 : val < 100 ? 1 : 0, + }) + + " " + + pickedUnit + ); +}; 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 b45ac3a4c5..c28a0c772e 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts @@ -17,7 +17,6 @@ import type { PropertyValues } from "lit"; import { css, html, LitElement, svg, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import { formatNumber } from "../../../../common/number/format_number"; import "../../../../components/ha-card"; import "../../../../components/ha-svg-icon"; import type { EnergyData } from "../../../../data/energy"; @@ -26,6 +25,7 @@ import { getEnergyDataCollection, getEnergyGasUnit, getEnergyWaterUnit, + formatConsumptionShort, } from "../../../../data/energy"; import { calculateStatisticsSumGrowth } from "../../../../data/recorder"; import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; @@ -308,10 +308,11 @@ class HuiEnergyDistrubutionCard rel="noopener no referrer" > - ${formatNumber(lowCarbonEnergy || 0, this.hass.locale, { - maximumFractionDigits: 1, - })} - kWh + ${formatConsumptionShort( + this.hass, + lowCarbonEnergy, + "kWh" + )} @@ -326,12 +327,11 @@ class HuiEnergyDistrubutionCard > - ${formatNumber( - totalSolarProduction || 0, - this.hass.locale, - { maximumFractionDigits: 1 } + ${formatConsumptionShort( + this.hass, + totalSolarProduction, + "kWh" )} - kWh ` : hasGas || hasWater @@ -346,13 +346,14 @@ class HuiEnergyDistrubutionCard > - ${formatNumber(gasUsage || 0, this.hass.locale, { - maximumFractionDigits: 1, - })} - ${getEnergyGasUnit( + ${formatConsumptionShort( this.hass, - prefs, - this._data.statsMetadata + gasUsage, + getEnergyGasUnit( + this.hass, + prefs, + this._data.statsMetadata + ) )} @@ -383,10 +384,11 @@ class HuiEnergyDistrubutionCard > - ${formatNumber(waterUsage || 0, this.hass.locale, { - maximumFractionDigits: 1, - })} - ${getEnergyWaterUnit(this.hass)} + ${formatConsumptionShort( + this.hass, + waterUsage, + getEnergyWaterUnit(this.hass) + )} @@ -420,10 +422,11 @@ class HuiEnergyDistrubutionCard class="small" .path=${mdiArrowLeft} >${formatNumber(returnedToGrid, this.hass.locale, { - maximumFractionDigits: 1, - })} - kWh + >${formatConsumptionShort( + this.hass, + returnedToGrid, + "kWh" + )} ` : ""} @@ -432,10 +435,11 @@ class HuiEnergyDistrubutionCard class="small" .path=${mdiArrowRight} >` - : ""}${formatNumber(totalFromGrid, this.hass.locale, { - maximumFractionDigits: 1, - })} - kWh + : ""}${formatConsumptionShort( + this.hass, + totalFromGrid, + "kWh" + )} - ${formatNumber(totalHomeConsumption, this.hass.locale, { - maximumFractionDigits: 1, - })} - kWh + ${formatConsumptionShort( + this.hass, + totalHomeConsumption, + "kWh" + )} ${homeSolarCircumference !== undefined || homeLowCarbonCircumference !== undefined ? html` @@ -550,29 +555,23 @@ class HuiEnergyDistrubutionCard class="small" .path=${mdiArrowDown} >${formatNumber( - totalBatteryIn || 0, - this.hass.locale, - { - maximumFractionDigits: 1, - } + >${formatConsumptionShort( + this.hass, + totalBatteryIn, + "kWh" )} - kWh + ${formatNumber( - totalBatteryOut || 0, - this.hass.locale, - { - maximumFractionDigits: 1, - } + >${formatConsumptionShort( + this.hass, + totalBatteryOut, + "kWh" )} - kWh + ${this.hass.localize( @@ -603,10 +602,11 @@ class HuiEnergyDistrubutionCard - ${formatNumber(waterUsage || 0, this.hass.locale, { - maximumFractionDigits: 1, - })} - ${getEnergyWaterUnit(this.hass)} + ${formatConsumptionShort( + this.hass, + waterUsage, + getEnergyWaterUnit(this.hass) + )} ${this.hass.localize( diff --git a/test/data/energy.test.ts b/test/data/energy.test.ts new file mode 100644 index 0000000000..a11c1a7142 --- /dev/null +++ b/test/data/energy.test.ts @@ -0,0 +1,82 @@ +import { assert, describe, it } from "vitest"; + +import { + type FrontendLocaleData, + NumberFormat, + TimeFormat, + FirstWeekday, + DateFormat, + TimeZone, +} from "../../src/data/translation"; +import { formatConsumptionShort } from "../../src/data/energy"; +import type { HomeAssistant } from "../../src/types"; + +describe("Energy Short Format Test", () => { + // Create default to not have to specify a not relevant TimeFormat over and over again. + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }; + + const hass = { locale: defaultLocale } as HomeAssistant; + it("Formats", () => { + assert.strictEqual(formatConsumptionShort(hass, 0, "kWh"), "0 kWh"); + assert.strictEqual(formatConsumptionShort(hass, 0, "GWh"), "0 GWh"); + assert.strictEqual(formatConsumptionShort(hass, 0, "gal"), "0 gal"); + + assert.strictEqual( + formatConsumptionShort(hass, 0.12345, "kWh"), + "0.12 kWh" + ); + assert.strictEqual( + formatConsumptionShort(hass, 10.12345, "kWh"), + "10.1 kWh" + ); + assert.strictEqual( + formatConsumptionShort(hass, 500.12345, "kWh"), + "500 kWh" + ); + assert.strictEqual( + formatConsumptionShort(hass, 1512.34567, "kWh"), + "1.51 MWh" + ); + assert.strictEqual( + formatConsumptionShort(hass, 15123.4567, "kWh"), + "15.1 MWh" + ); + assert.strictEqual( + formatConsumptionShort(hass, 151234.5678, "kWh"), + "151 MWh" + ); + assert.strictEqual( + formatConsumptionShort(hass, 1512345.6789, "kWh"), + "1.51 GWh" + ); + assert.strictEqual( + formatConsumptionShort(hass, 15123456789.9, "kWh"), + "15.1 TWh" + ); + + assert.strictEqual( + formatConsumptionShort(hass, 15123456789000.9, "kWh"), + "15,123 TWh" + ); + + assert.strictEqual(formatConsumptionShort(hass, 1000.1, "GWh"), "1 TWh"); + + assert.strictEqual( + formatConsumptionShort(hass, 10000.12345, "gal"), + "10,000 gal" + ); + + // Don't really modify negative numbers, but make sure it's something sane. + assert.strictEqual( + formatConsumptionShort(hass, -1234.56, "kWh"), + "-1,234.56 kWh" + ); + }); +});