Skip to content

Commit 9f05f4d

Browse files
authored
Short-format numbers in energy-distribution-card (#24716)
1 parent 6fbc7b2 commit 9f05f4d

File tree

3 files changed

+163
-52
lines changed

3 files changed

+163
-52
lines changed

src/data/energy.ts

+29
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import type {
2929
import { fetchStatistics, getStatisticMetadata } from "./recorder";
3030
import { calcDateRange } from "../common/datetime/calc_date_range";
3131
import type { DateRange } from "../common/datetime/calc_date_range";
32+
import { formatNumber } from "../common/number/format_number";
3233

3334
const energyCollectionKeys: (string | undefined)[] = [];
3435

@@ -924,3 +925,31 @@ const computeConsumptionDataPartial = (
924925

925926
return outData;
926927
};
928+
929+
export const formatConsumptionShort = (
930+
hass: HomeAssistant,
931+
consumption: number | null,
932+
unit: string
933+
): string => {
934+
if (!consumption) {
935+
return `0 ${unit}`;
936+
}
937+
const units = ["kWh", "MWh", "GWh", "TWh"];
938+
let pickedUnit = unit;
939+
let val = consumption;
940+
let unitIndex = units.findIndex((u) => u === unit);
941+
if (unitIndex >= 0) {
942+
while (val >= 1000 && unitIndex < units.length - 1) {
943+
val /= 1000;
944+
unitIndex++;
945+
}
946+
pickedUnit = units[unitIndex];
947+
}
948+
return (
949+
formatNumber(val, hass.locale, {
950+
maximumFractionDigits: val < 10 ? 2 : val < 100 ? 1 : 0,
951+
}) +
952+
" " +
953+
pickedUnit
954+
);
955+
};

src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts

+52-52
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import type { PropertyValues } from "lit";
1717
import { css, html, LitElement, svg, nothing } from "lit";
1818
import { customElement, property, state } from "lit/decorators";
1919
import { classMap } from "lit/directives/class-map";
20-
import { formatNumber } from "../../../../common/number/format_number";
2120
import "../../../../components/ha-card";
2221
import "../../../../components/ha-svg-icon";
2322
import type { EnergyData } from "../../../../data/energy";
@@ -26,6 +25,7 @@ import {
2625
getEnergyDataCollection,
2726
getEnergyGasUnit,
2827
getEnergyWaterUnit,
28+
formatConsumptionShort,
2929
} from "../../../../data/energy";
3030
import { calculateStatisticsSumGrowth } from "../../../../data/recorder";
3131
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
@@ -308,10 +308,11 @@ class HuiEnergyDistrubutionCard
308308
rel="noopener no referrer"
309309
>
310310
<ha-svg-icon .path=${mdiLeaf}></ha-svg-icon>
311-
${formatNumber(lowCarbonEnergy || 0, this.hass.locale, {
312-
maximumFractionDigits: 1,
313-
})}
314-
kWh
311+
${formatConsumptionShort(
312+
this.hass,
313+
lowCarbonEnergy,
314+
"kWh"
315+
)}
315316
</a>
316317
<svg width="80" height="30">
317318
<line x1="40" y1="0" x2="40" y2="30"></line>
@@ -326,12 +327,11 @@ class HuiEnergyDistrubutionCard
326327
>
327328
<div class="circle">
328329
<ha-svg-icon .path=${mdiSolarPower}></ha-svg-icon>
329-
${formatNumber(
330-
totalSolarProduction || 0,
331-
this.hass.locale,
332-
{ maximumFractionDigits: 1 }
330+
${formatConsumptionShort(
331+
this.hass,
332+
totalSolarProduction,
333+
"kWh"
333334
)}
334-
kWh
335335
</div>
336336
</div>`
337337
: hasGas || hasWater
@@ -346,13 +346,14 @@ class HuiEnergyDistrubutionCard
346346
>
347347
<div class="circle">
348348
<ha-svg-icon .path=${mdiFire}></ha-svg-icon>
349-
${formatNumber(gasUsage || 0, this.hass.locale, {
350-
maximumFractionDigits: 1,
351-
})}
352-
${getEnergyGasUnit(
349+
${formatConsumptionShort(
353350
this.hass,
354-
prefs,
355-
this._data.statsMetadata
351+
gasUsage,
352+
getEnergyGasUnit(
353+
this.hass,
354+
prefs,
355+
this._data.statsMetadata
356+
)
356357
)}
357358
</div>
358359
<svg width="80" height="30">
@@ -383,10 +384,11 @@ class HuiEnergyDistrubutionCard
383384
>
384385
<div class="circle">
385386
<ha-svg-icon .path=${mdiWater}></ha-svg-icon>
386-
${formatNumber(waterUsage || 0, this.hass.locale, {
387-
maximumFractionDigits: 1,
388-
})}
389-
${getEnergyWaterUnit(this.hass)}
387+
${formatConsumptionShort(
388+
this.hass,
389+
waterUsage,
390+
getEnergyWaterUnit(this.hass)
391+
)}
390392
</div>
391393
<svg width="80" height="30">
392394
<path d="M40 0 v30" id="water" />
@@ -420,10 +422,11 @@ class HuiEnergyDistrubutionCard
420422
class="small"
421423
.path=${mdiArrowLeft}
422424
></ha-svg-icon
423-
>${formatNumber(returnedToGrid, this.hass.locale, {
424-
maximumFractionDigits: 1,
425-
})}
426-
kWh
425+
>${formatConsumptionShort(
426+
this.hass,
427+
returnedToGrid,
428+
"kWh"
429+
)}
427430
</span>`
428431
: ""}
429432
<span class="consumption">
@@ -432,10 +435,11 @@ class HuiEnergyDistrubutionCard
432435
class="small"
433436
.path=${mdiArrowRight}
434437
></ha-svg-icon>`
435-
: ""}${formatNumber(totalFromGrid, this.hass.locale, {
436-
maximumFractionDigits: 1,
437-
})}
438-
kWh
438+
: ""}${formatConsumptionShort(
439+
this.hass,
440+
totalFromGrid,
441+
"kWh"
442+
)}
439443
</span>
440444
</div>
441445
<span class="label"
@@ -453,10 +457,11 @@ class HuiEnergyDistrubutionCard
453457
})}"
454458
>
455459
<ha-svg-icon .path=${mdiHome}></ha-svg-icon>
456-
${formatNumber(totalHomeConsumption, this.hass.locale, {
457-
maximumFractionDigits: 1,
458-
})}
459-
kWh
460+
${formatConsumptionShort(
461+
this.hass,
462+
totalHomeConsumption,
463+
"kWh"
464+
)}
460465
${homeSolarCircumference !== undefined ||
461466
homeLowCarbonCircumference !== undefined
462467
? html`<svg>
@@ -550,29 +555,23 @@ class HuiEnergyDistrubutionCard
550555
class="small"
551556
.path=${mdiArrowDown}
552557
></ha-svg-icon
553-
>${formatNumber(
554-
totalBatteryIn || 0,
555-
this.hass.locale,
556-
{
557-
maximumFractionDigits: 1,
558-
}
558+
>${formatConsumptionShort(
559+
this.hass,
560+
totalBatteryIn,
561+
"kWh"
559562
)}
560-
kWh</span
561-
>
563+
</span>
562564
<span class="battery-out">
563565
<ha-svg-icon
564566
class="small"
565567
.path=${mdiArrowUp}
566568
></ha-svg-icon
567-
>${formatNumber(
568-
totalBatteryOut || 0,
569-
this.hass.locale,
570-
{
571-
maximumFractionDigits: 1,
572-
}
569+
>${formatConsumptionShort(
570+
this.hass,
571+
totalBatteryOut,
572+
"kWh"
573573
)}
574-
kWh</span
575-
>
574+
</span>
576575
</div>
577576
<span class="label"
578577
>${this.hass.localize(
@@ -603,10 +602,11 @@ class HuiEnergyDistrubutionCard
603602
</svg>
604603
<div class="circle">
605604
<ha-svg-icon .path=${mdiWater}></ha-svg-icon>
606-
${formatNumber(waterUsage || 0, this.hass.locale, {
607-
maximumFractionDigits: 1,
608-
})}
609-
${getEnergyWaterUnit(this.hass)}
605+
${formatConsumptionShort(
606+
this.hass,
607+
waterUsage,
608+
getEnergyWaterUnit(this.hass)
609+
)}
610610
</div>
611611
<span class="label"
612612
>${this.hass.localize(

test/data/energy.test.ts

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { assert, describe, it } from "vitest";
2+
3+
import {
4+
type FrontendLocaleData,
5+
NumberFormat,
6+
TimeFormat,
7+
FirstWeekday,
8+
DateFormat,
9+
TimeZone,
10+
} from "../../src/data/translation";
11+
import { formatConsumptionShort } from "../../src/data/energy";
12+
import type { HomeAssistant } from "../../src/types";
13+
14+
describe("Energy Short Format Test", () => {
15+
// Create default to not have to specify a not relevant TimeFormat over and over again.
16+
const defaultLocale: FrontendLocaleData = {
17+
language: "en",
18+
number_format: NumberFormat.language,
19+
time_format: TimeFormat.language,
20+
date_format: DateFormat.language,
21+
time_zone: TimeZone.local,
22+
first_weekday: FirstWeekday.language,
23+
};
24+
25+
const hass = { locale: defaultLocale } as HomeAssistant;
26+
it("Formats", () => {
27+
assert.strictEqual(formatConsumptionShort(hass, 0, "kWh"), "0 kWh");
28+
assert.strictEqual(formatConsumptionShort(hass, 0, "GWh"), "0 GWh");
29+
assert.strictEqual(formatConsumptionShort(hass, 0, "gal"), "0 gal");
30+
31+
assert.strictEqual(
32+
formatConsumptionShort(hass, 0.12345, "kWh"),
33+
"0.12 kWh"
34+
);
35+
assert.strictEqual(
36+
formatConsumptionShort(hass, 10.12345, "kWh"),
37+
"10.1 kWh"
38+
);
39+
assert.strictEqual(
40+
formatConsumptionShort(hass, 500.12345, "kWh"),
41+
"500 kWh"
42+
);
43+
assert.strictEqual(
44+
formatConsumptionShort(hass, 1512.34567, "kWh"),
45+
"1.51 MWh"
46+
);
47+
assert.strictEqual(
48+
formatConsumptionShort(hass, 15123.4567, "kWh"),
49+
"15.1 MWh"
50+
);
51+
assert.strictEqual(
52+
formatConsumptionShort(hass, 151234.5678, "kWh"),
53+
"151 MWh"
54+
);
55+
assert.strictEqual(
56+
formatConsumptionShort(hass, 1512345.6789, "kWh"),
57+
"1.51 GWh"
58+
);
59+
assert.strictEqual(
60+
formatConsumptionShort(hass, 15123456789.9, "kWh"),
61+
"15.1 TWh"
62+
);
63+
64+
assert.strictEqual(
65+
formatConsumptionShort(hass, 15123456789000.9, "kWh"),
66+
"15,123 TWh"
67+
);
68+
69+
assert.strictEqual(formatConsumptionShort(hass, 1000.1, "GWh"), "1 TWh");
70+
71+
assert.strictEqual(
72+
formatConsumptionShort(hass, 10000.12345, "gal"),
73+
"10,000 gal"
74+
);
75+
76+
// Don't really modify negative numbers, but make sure it's something sane.
77+
assert.strictEqual(
78+
formatConsumptionShort(hass, -1234.56, "kWh"),
79+
"-1,234.56 kWh"
80+
);
81+
});
82+
});

0 commit comments

Comments
 (0)