mirror of
https://github.com/home-assistant/frontend.git
synced 2026-02-14 23:18:21 +00:00
Normalize SI unit prefixes in distribution card proportions (#29539)
* Normalize SI unit prefixes in distribution card proportions * Extract SI prefix normalization to shared utility with tests Moves normalizeValueBySIPrefix to src/common/number/ so it can be reused. Replaces the inline method in the distribution card and the switch statement in getPowerFromState (energy.ts).
This commit is contained in:
28
src/common/number/normalize-by-si-prefix.ts
Normal file
28
src/common/number/normalize-by-si-prefix.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
const SI_PREFIX_MULTIPLIERS: Record<string, number> = {
|
||||
T: 1e12,
|
||||
G: 1e9,
|
||||
M: 1e6,
|
||||
k: 1e3,
|
||||
m: 1e-3,
|
||||
"\u00B5": 1e-6, // µ (micro sign)
|
||||
"\u03BC": 1e-6, // μ (greek small letter mu)
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize a numeric value by detecting SI unit prefixes (T, G, M, k, m, µ).
|
||||
* Only applies when the unit is longer than 1 character and starts with a
|
||||
* recognized prefix, avoiding false positives on standalone units like "m" (meters).
|
||||
*/
|
||||
export const normalizeValueBySIPrefix = (
|
||||
value: number,
|
||||
unit: string | undefined
|
||||
): number => {
|
||||
if (!unit || unit.length <= 1) {
|
||||
return value;
|
||||
}
|
||||
const prefix = unit[0];
|
||||
if (prefix in SI_PREFIX_MULTIPLIERS) {
|
||||
return value * SI_PREFIX_MULTIPLIERS[prefix];
|
||||
}
|
||||
return value;
|
||||
};
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import type { Collection, HassEntity } from "home-assistant-js-websocket";
|
||||
import { getCollection } from "home-assistant-js-websocket";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { normalizeValueBySIPrefix } from "../common/number/normalize-by-si-prefix";
|
||||
import {
|
||||
calcDate,
|
||||
calcDateProperty,
|
||||
@@ -1431,26 +1432,10 @@ export const getPowerFromState = (stateObj: HassEntity): number | undefined => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Normalize to watts (W) based on unit of measurement (case-sensitive)
|
||||
// Supported units: GW, kW, MW, mW, TW, W
|
||||
const unit = stateObj.attributes.unit_of_measurement;
|
||||
switch (unit) {
|
||||
case "W":
|
||||
return value;
|
||||
case "kW":
|
||||
return value * 1000;
|
||||
case "mW":
|
||||
return value / 1000;
|
||||
case "MW":
|
||||
return value * 1_000_000;
|
||||
case "GW":
|
||||
return value * 1_000_000_000;
|
||||
case "TW":
|
||||
return value * 1_000_000_000_000;
|
||||
default:
|
||||
// Assume value is in watts (W) if no unit or an unsupported unit is provided
|
||||
return value;
|
||||
}
|
||||
return normalizeValueBySIPrefix(
|
||||
value,
|
||||
stateObj.attributes.unit_of_measurement
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { getGraphColorByIndex } from "../../../common/color/colors";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { normalizeValueBySIPrefix } from "../../../common/number/normalize-by-si-prefix";
|
||||
import { MobileAwareMixin } from "../../../mixins/mobile-aware-mixin";
|
||||
import type { EntityNameItem } from "../../../common/entity/compute_entity_name_display";
|
||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
|
||||
@@ -230,8 +231,12 @@ export class HuiDistributionCard
|
||||
const stateObj = this.hass!.states[entity.entity];
|
||||
if (!stateObj) return;
|
||||
|
||||
const value = Number(stateObj.state);
|
||||
if (value <= 0 || isNaN(value)) return;
|
||||
const rawValue = Number(stateObj.state);
|
||||
if (rawValue <= 0 || isNaN(rawValue)) return;
|
||||
const value = normalizeValueBySIPrefix(
|
||||
rawValue,
|
||||
stateObj.attributes.unit_of_measurement
|
||||
);
|
||||
|
||||
const color = entity.color
|
||||
? computeCssColor(entity.color)
|
||||
|
||||
54
test/common/number/normalize-by-si-prefix.test.ts
Normal file
54
test/common/number/normalize-by-si-prefix.test.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { assert, describe, it } from "vitest";
|
||||
|
||||
import { normalizeValueBySIPrefix } from "../../../src/common/number/normalize-by-si-prefix";
|
||||
|
||||
describe("normalizeValueBySIPrefix", () => {
|
||||
it("Applies kilo prefix (k)", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(11, "kW"), 11000);
|
||||
assert.equal(normalizeValueBySIPrefix(2.5, "kWh"), 2500);
|
||||
});
|
||||
|
||||
it("Applies mega prefix (M)", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(3, "MW"), 3_000_000);
|
||||
});
|
||||
|
||||
it("Applies giga prefix (G)", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(1, "GW"), 1_000_000_000);
|
||||
});
|
||||
|
||||
it("Applies tera prefix (T)", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(2, "TW"), 2_000_000_000_000);
|
||||
});
|
||||
|
||||
it("Applies milli prefix (m)", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(500, "mW"), 0.5);
|
||||
});
|
||||
|
||||
it("Applies micro prefix (µ micro sign U+00B5)", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(1000, "\u00B5W"), 0.001);
|
||||
});
|
||||
|
||||
it("Applies micro prefix (μ greek mu U+03BC)", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(1000, "\u03BCW"), 0.001);
|
||||
});
|
||||
|
||||
it("Returns value unchanged for single-char units", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(100, "W"), 100);
|
||||
assert.equal(normalizeValueBySIPrefix(5, "m"), 5);
|
||||
assert.equal(normalizeValueBySIPrefix(22, "K"), 22);
|
||||
});
|
||||
|
||||
it("Returns value unchanged for undefined unit", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(42, undefined), 42);
|
||||
});
|
||||
|
||||
it("Returns value unchanged for unrecognized prefixes", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(20, "°C"), 20);
|
||||
assert.equal(normalizeValueBySIPrefix(50, "dB"), 50);
|
||||
assert.equal(normalizeValueBySIPrefix(1013, "hPa"), 1013);
|
||||
});
|
||||
|
||||
it("Returns value unchanged for empty string", () => {
|
||||
assert.equal(normalizeValueBySIPrefix(10, ""), 10);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user