mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-19 18:28:42 +00:00
Switch energy now chart to watts, format values to W, kW etc (#28555)
* Switch energy now chart to watts * Add kW * Scale formatted value based on powers of 1000 1000 W -> 1 kW W → kW → MW → GW → TW * Explainers * Use 3 dp for kW+ and 0 for W * Add non-integer test
This commit is contained in:
@@ -1363,9 +1363,9 @@ export const calculateSolarConsumedGauge = (
|
||||
};
|
||||
|
||||
/**
|
||||
* Get current power value from entity state, normalized to kW
|
||||
* Get current power value from entity state, normalized to watts (W)
|
||||
* @param stateObj - The entity state object to get power value from
|
||||
* @returns Power value in kW, or 0 if entity not found or invalid
|
||||
* @returns Power value in W (watts), or undefined if entity not found or invalid
|
||||
*/
|
||||
export const getPowerFromState = (stateObj: HassEntity): number | undefined => {
|
||||
if (!stateObj) {
|
||||
@@ -1376,22 +1376,54 @@ export const getPowerFromState = (stateObj: HassEntity): number | undefined => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Normalize to kW based on unit of measurement (case-sensitive)
|
||||
// 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 / 1000;
|
||||
case "mW":
|
||||
return value / 1000000;
|
||||
case "MW":
|
||||
return value;
|
||||
case "kW":
|
||||
return value * 1000;
|
||||
case "mW":
|
||||
return value / 1000;
|
||||
case "MW":
|
||||
return value * 1_000_000;
|
||||
case "GW":
|
||||
return value * 1000000;
|
||||
return value * 1_000_000_000;
|
||||
case "TW":
|
||||
return value * 1000000000;
|
||||
return value * 1_000_000_000_000;
|
||||
default:
|
||||
// Assume kW if no unit or unit is kW
|
||||
// Assume value is in watts (W) if no unit or an unsupported unit is provided
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Format power value in watts (W) to a short string with the appropriate unit
|
||||
* @param hass - The HomeAssistant instance
|
||||
* @param powerWatts - The power value in watts (W)
|
||||
* @returns A string with the formatted power value and unit
|
||||
*/
|
||||
export const formatPowerShort = (
|
||||
hass: HomeAssistant,
|
||||
powerWatts: number
|
||||
): string => {
|
||||
const units = ["W", "kW", "MW", "GW", "TW"];
|
||||
let unitIndex = 0;
|
||||
let value = powerWatts;
|
||||
|
||||
// Scale the unit to the appropriate power of 1000
|
||||
while (Math.abs(value) >= 1000 && unitIndex < units.length - 1) {
|
||||
value /= 1000;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return (
|
||||
formatNumber(value, hass.locale, {
|
||||
// For watts, show no decimals. For kW and above, always show 3 decimals.
|
||||
maximumFractionDigits: units[unitIndex] === "W" ? 0 : 3,
|
||||
}) +
|
||||
" " +
|
||||
units[unitIndex]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
|
||||
import {
|
||||
formatPowerShort,
|
||||
getEnergyDataCollection,
|
||||
getPowerFromState,
|
||||
} from "../../../../data/energy";
|
||||
@@ -17,7 +18,6 @@ import type { PowerSankeyCardConfig } from "../types";
|
||||
import "../../../../components/chart/ha-sankey-chart";
|
||||
import type { Link, Node } from "../../../../components/chart/ha-sankey-chart";
|
||||
import { getGraphColorByIndex } from "../../../../common/color/colors";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import { getEntityContext } from "../../../../common/entity/context/get_entity_context";
|
||||
import { MobileAwareMixin } from "../../../../mixins/mobile-aware-mixin";
|
||||
|
||||
@@ -26,8 +26,8 @@ const DEFAULT_CONFIG: Partial<PowerSankeyCardConfig> = {
|
||||
group_by_area: true,
|
||||
};
|
||||
|
||||
// Minimum power threshold in kW to display a device node
|
||||
const MIN_POWER_THRESHOLD = 0.01;
|
||||
// Minimum power threshold in watts (W) to display a device node
|
||||
const MIN_POWER_THRESHOLD = 10;
|
||||
|
||||
interface PowerData {
|
||||
solar: number;
|
||||
@@ -472,8 +472,8 @@ class HuiPowerSankeyCard
|
||||
|
||||
private _valueFormatter = (value: number) =>
|
||||
`<div style="direction:ltr; display: inline;">
|
||||
${formatNumber(value, this.hass.locale, value < 0.1 ? { maximumFractionDigits: 3 } : undefined)}
|
||||
kW</div>`;
|
||||
${formatPowerShort(this.hass, value)}
|
||||
</div>`;
|
||||
|
||||
/**
|
||||
* Compute real-time power data from current entity states.
|
||||
@@ -719,14 +719,15 @@ class HuiPowerSankeyCard
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current power value from entity state, normalized to kW
|
||||
* Get current power value from entity state, normalized to watts (W)
|
||||
* @param entityId - The entity ID to get power value from
|
||||
* @returns Power value in kW, or 0 if entity not found or invalid
|
||||
* @returns Power value in W, or 0 if entity not found or invalid
|
||||
*/
|
||||
private _getCurrentPower(entityId: string): number {
|
||||
// Track this entity for state change detection
|
||||
this._entities.add(entityId);
|
||||
|
||||
// getPowerFromState returns power in W
|
||||
return getPowerFromState(this.hass.states[entityId]) ?? 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -210,9 +210,14 @@ export class HuiPowerSourcesGraphCard
|
||||
const { positive, negative } = this._processData(
|
||||
statIds[key].stats.map((id: string) => {
|
||||
const stats = energyData.stats[id] ?? [];
|
||||
const currentState = getPowerFromState(this.hass.states[id]);
|
||||
if (currentState !== undefined) {
|
||||
stats.push({ start: now, end: now, mean: currentState });
|
||||
const currentStateWatts = getPowerFromState(this.hass.states[id]);
|
||||
if (currentStateWatts !== undefined) {
|
||||
// getPowerFromState returns power in W; convert to kW for this graph
|
||||
stats.push({
|
||||
start: now,
|
||||
end: now,
|
||||
mean: currentStateWatts / 1000,
|
||||
});
|
||||
}
|
||||
return stats;
|
||||
})
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
computeConsumptionSingle,
|
||||
formatConsumptionShort,
|
||||
calculateSolarConsumedGauge,
|
||||
formatPowerShort,
|
||||
} from "../../src/data/energy";
|
||||
import type { HomeAssistant } from "../../src/types";
|
||||
|
||||
@@ -171,6 +172,17 @@ describe("Energy Short Format Test", () => {
|
||||
"151 MWh"
|
||||
);
|
||||
});
|
||||
it("Power Short Format", () => {
|
||||
assert.strictEqual(formatPowerShort(hass, 0), "0 W");
|
||||
assert.strictEqual(formatPowerShort(hass, 10), "10 W");
|
||||
assert.strictEqual(formatPowerShort(hass, 12.2), "12 W");
|
||||
assert.strictEqual(formatPowerShort(hass, 999), "999 W");
|
||||
assert.strictEqual(formatPowerShort(hass, 1000), "1 kW");
|
||||
assert.strictEqual(formatPowerShort(hass, 1234), "1.234 kW");
|
||||
assert.strictEqual(formatPowerShort(hass, 10_500), "10.5 kW");
|
||||
assert.strictEqual(formatPowerShort(hass, 1_500_000), "1.5 MW");
|
||||
assert.strictEqual(formatPowerShort(hass, -1500), "-1.5 kW");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Energy Usage Calculation Tests", () => {
|
||||
|
||||
Reference in New Issue
Block a user