742 lines
19 KiB
TypeScript
742 lines
19 KiB
TypeScript
import {
|
|
addDays,
|
|
addHours,
|
|
addMilliseconds,
|
|
addMonths,
|
|
differenceInDays,
|
|
endOfDay,
|
|
startOfDay,
|
|
} from "date-fns/esm";
|
|
import { Collection, getCollection } from "home-assistant-js-websocket";
|
|
import { calcDate } from "../common/datetime/calc_date";
|
|
import { formatTime24h } from "../common/datetime/format_time";
|
|
import { groupBy } from "../common/util/group-by";
|
|
import { HomeAssistant } from "../types";
|
|
import { ConfigEntry, getConfigEntries } from "./config_entries";
|
|
import {
|
|
fetchStatistics,
|
|
getStatisticMetadata,
|
|
Statistics,
|
|
StatisticsMetaData,
|
|
StatisticsUnitConfiguration,
|
|
} from "./recorder";
|
|
|
|
const energyCollectionKeys: (string | undefined)[] = [];
|
|
|
|
export const emptyFlowFromGridSourceEnergyPreference =
|
|
(): FlowFromGridSourceEnergyPreference => ({
|
|
stat_energy_from: "",
|
|
stat_cost: null,
|
|
entity_energy_price: null,
|
|
number_energy_price: null,
|
|
});
|
|
|
|
export const emptyFlowToGridSourceEnergyPreference =
|
|
(): FlowToGridSourceEnergyPreference => ({
|
|
stat_energy_to: "",
|
|
stat_compensation: null,
|
|
entity_energy_price: null,
|
|
number_energy_price: null,
|
|
});
|
|
|
|
export const emptyGridSourceEnergyPreference =
|
|
(): GridSourceTypeEnergyPreference => ({
|
|
type: "grid",
|
|
flow_from: [],
|
|
flow_to: [],
|
|
cost_adjustment_day: 0,
|
|
});
|
|
|
|
export const emptySolarEnergyPreference =
|
|
(): SolarSourceTypeEnergyPreference => ({
|
|
type: "solar",
|
|
stat_energy_from: "",
|
|
config_entry_solar_forecast: null,
|
|
});
|
|
|
|
export const emptyBatteryEnergyPreference =
|
|
(): BatterySourceTypeEnergyPreference => ({
|
|
type: "battery",
|
|
stat_energy_from: "",
|
|
stat_energy_to: "",
|
|
});
|
|
|
|
export const emptyGasEnergyPreference = (): GasSourceTypeEnergyPreference => ({
|
|
type: "gas",
|
|
stat_energy_from: "",
|
|
stat_cost: null,
|
|
entity_energy_price: null,
|
|
number_energy_price: null,
|
|
});
|
|
|
|
export const emptyWaterEnergyPreference =
|
|
(): WaterSourceTypeEnergyPreference => ({
|
|
type: "water",
|
|
stat_energy_from: "",
|
|
stat_cost: null,
|
|
entity_energy_price: null,
|
|
number_energy_price: null,
|
|
});
|
|
|
|
interface EnergySolarForecast {
|
|
wh_hours: Record<string, number>;
|
|
}
|
|
export type EnergySolarForecasts = {
|
|
[config_entry_id: string]: EnergySolarForecast;
|
|
};
|
|
|
|
export interface DeviceConsumptionEnergyPreference {
|
|
// This is an ever increasing value
|
|
stat_consumption: string;
|
|
}
|
|
|
|
export interface FlowFromGridSourceEnergyPreference {
|
|
// kWh meter
|
|
stat_energy_from: string;
|
|
|
|
// $ meter
|
|
stat_cost: string | null;
|
|
|
|
// Can be used to generate costs if stat_cost omitted
|
|
entity_energy_price: string | null;
|
|
number_energy_price: number | null;
|
|
}
|
|
|
|
export interface FlowToGridSourceEnergyPreference {
|
|
// kWh meter
|
|
stat_energy_to: string;
|
|
|
|
// $ meter
|
|
stat_compensation: string | null;
|
|
|
|
// Can be used to generate costs if stat_compensation omitted
|
|
entity_energy_price: string | null;
|
|
number_energy_price: number | null;
|
|
}
|
|
|
|
export interface GridSourceTypeEnergyPreference {
|
|
type: "grid";
|
|
|
|
flow_from: FlowFromGridSourceEnergyPreference[];
|
|
flow_to: FlowToGridSourceEnergyPreference[];
|
|
|
|
cost_adjustment_day: number;
|
|
}
|
|
|
|
export interface SolarSourceTypeEnergyPreference {
|
|
type: "solar";
|
|
|
|
stat_energy_from: string;
|
|
config_entry_solar_forecast: string[] | null;
|
|
}
|
|
|
|
export interface BatterySourceTypeEnergyPreference {
|
|
type: "battery";
|
|
stat_energy_from: string;
|
|
stat_energy_to: string;
|
|
}
|
|
export interface GasSourceTypeEnergyPreference {
|
|
type: "gas";
|
|
|
|
// kWh/volume meter
|
|
stat_energy_from: string;
|
|
|
|
// $ meter
|
|
stat_cost: string | null;
|
|
|
|
// Can be used to generate costs if stat_cost omitted
|
|
entity_energy_price: string | null;
|
|
number_energy_price: number | null;
|
|
unit_of_measurement?: string | null;
|
|
}
|
|
|
|
export interface WaterSourceTypeEnergyPreference {
|
|
type: "water";
|
|
|
|
// volume meter
|
|
stat_energy_from: string;
|
|
|
|
// $ meter
|
|
stat_cost: string | null;
|
|
|
|
// Can be used to generate costs if stat_cost omitted
|
|
entity_energy_price: string | null;
|
|
number_energy_price: number | null;
|
|
unit_of_measurement?: string | null;
|
|
}
|
|
|
|
type EnergySource =
|
|
| SolarSourceTypeEnergyPreference
|
|
| GridSourceTypeEnergyPreference
|
|
| BatterySourceTypeEnergyPreference
|
|
| GasSourceTypeEnergyPreference
|
|
| WaterSourceTypeEnergyPreference;
|
|
|
|
export interface EnergyPreferences {
|
|
energy_sources: EnergySource[];
|
|
device_consumption: DeviceConsumptionEnergyPreference[];
|
|
}
|
|
|
|
export interface EnergyInfo {
|
|
cost_sensors: Record<string, string>;
|
|
solar_forecast_domains: string[];
|
|
}
|
|
|
|
export interface EnergyValidationIssue {
|
|
type: string;
|
|
affected_entities: [string, unknown][];
|
|
translation_placeholders: Record<string, string>;
|
|
}
|
|
|
|
export interface EnergyPreferencesValidation {
|
|
energy_sources: EnergyValidationIssue[][];
|
|
device_consumption: EnergyValidationIssue[][];
|
|
}
|
|
|
|
export const getEnergyInfo = (hass: HomeAssistant) =>
|
|
hass.callWS<EnergyInfo>({
|
|
type: "energy/info",
|
|
});
|
|
|
|
export const getEnergyPreferenceValidation = async (hass: HomeAssistant) => {
|
|
await hass.loadBackendTranslation("issues", "energy");
|
|
return hass.callWS<EnergyPreferencesValidation>({
|
|
type: "energy/validate",
|
|
});
|
|
};
|
|
|
|
export const getEnergyPreferences = (hass: HomeAssistant) =>
|
|
hass.callWS<EnergyPreferences>({
|
|
type: "energy/get_prefs",
|
|
});
|
|
|
|
export const saveEnergyPreferences = async (
|
|
hass: HomeAssistant,
|
|
prefs: Partial<EnergyPreferences>
|
|
) => {
|
|
const newPrefs = hass.callWS<EnergyPreferences>({
|
|
type: "energy/save_prefs",
|
|
...prefs,
|
|
});
|
|
clearEnergyCollectionPreferences(hass);
|
|
return newPrefs;
|
|
};
|
|
|
|
export interface FossilEnergyConsumption {
|
|
[date: string]: number;
|
|
}
|
|
|
|
export const getFossilEnergyConsumption = async (
|
|
hass: HomeAssistant,
|
|
startTime: Date,
|
|
energy_statistic_ids: string[],
|
|
co2_statistic_id: string,
|
|
endTime?: Date,
|
|
period: "5minute" | "hour" | "day" | "month" = "hour"
|
|
) =>
|
|
hass.callWS<FossilEnergyConsumption>({
|
|
type: "energy/fossil_energy_consumption",
|
|
start_time: startTime.toISOString(),
|
|
end_time: endTime?.toISOString(),
|
|
energy_statistic_ids,
|
|
co2_statistic_id,
|
|
period,
|
|
});
|
|
|
|
interface EnergySourceByType {
|
|
grid?: GridSourceTypeEnergyPreference[];
|
|
solar?: SolarSourceTypeEnergyPreference[];
|
|
battery?: BatterySourceTypeEnergyPreference[];
|
|
gas?: GasSourceTypeEnergyPreference[];
|
|
water?: WaterSourceTypeEnergyPreference[];
|
|
}
|
|
|
|
export const energySourcesByType = (prefs: EnergyPreferences) =>
|
|
groupBy(prefs.energy_sources, (item) => item.type) as EnergySourceByType;
|
|
|
|
export interface EnergyData {
|
|
start: Date;
|
|
end?: Date;
|
|
startCompare?: Date;
|
|
endCompare?: Date;
|
|
prefs: EnergyPreferences;
|
|
info: EnergyInfo;
|
|
stats: Statistics;
|
|
statsMetadata: Record<string, StatisticsMetaData>;
|
|
statsCompare: Statistics;
|
|
co2SignalConfigEntry?: ConfigEntry;
|
|
co2SignalEntity?: string;
|
|
fossilEnergyConsumption?: FossilEnergyConsumption;
|
|
fossilEnergyConsumptionCompare?: FossilEnergyConsumption;
|
|
}
|
|
|
|
export const getReferencedStatisticIds = (
|
|
prefs: EnergyPreferences,
|
|
info: EnergyInfo,
|
|
includeTypes?: string[]
|
|
): string[] => {
|
|
const statIDs: string[] = [];
|
|
|
|
for (const source of prefs.energy_sources) {
|
|
if (includeTypes && !includeTypes.includes(source.type)) {
|
|
continue;
|
|
}
|
|
|
|
if (source.type === "solar") {
|
|
statIDs.push(source.stat_energy_from);
|
|
continue;
|
|
}
|
|
|
|
if (source.type === "gas" || source.type === "water") {
|
|
statIDs.push(source.stat_energy_from);
|
|
|
|
if (source.stat_cost) {
|
|
statIDs.push(source.stat_cost);
|
|
}
|
|
const costStatId = info.cost_sensors[source.stat_energy_from];
|
|
if (costStatId) {
|
|
statIDs.push(costStatId);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (source.type === "battery") {
|
|
statIDs.push(source.stat_energy_from);
|
|
statIDs.push(source.stat_energy_to);
|
|
continue;
|
|
}
|
|
|
|
// grid source
|
|
for (const flowFrom of source.flow_from) {
|
|
statIDs.push(flowFrom.stat_energy_from);
|
|
if (flowFrom.stat_cost) {
|
|
statIDs.push(flowFrom.stat_cost);
|
|
}
|
|
const costStatId = info.cost_sensors[flowFrom.stat_energy_from];
|
|
if (costStatId) {
|
|
statIDs.push(costStatId);
|
|
}
|
|
}
|
|
for (const flowTo of source.flow_to) {
|
|
statIDs.push(flowTo.stat_energy_to);
|
|
if (flowTo.stat_compensation) {
|
|
statIDs.push(flowTo.stat_compensation);
|
|
}
|
|
const costStatId = info.cost_sensors[flowTo.stat_energy_to];
|
|
if (costStatId) {
|
|
statIDs.push(costStatId);
|
|
}
|
|
}
|
|
}
|
|
|
|
return statIDs;
|
|
};
|
|
|
|
const getEnergyData = async (
|
|
hass: HomeAssistant,
|
|
prefs: EnergyPreferences,
|
|
start: Date,
|
|
end?: Date,
|
|
compare?: boolean
|
|
): Promise<EnergyData> => {
|
|
const [configEntries, info] = await Promise.all([
|
|
getConfigEntries(hass, { domain: "co2signal" }),
|
|
getEnergyInfo(hass),
|
|
]);
|
|
|
|
const co2SignalConfigEntry = configEntries.length
|
|
? configEntries[0]
|
|
: undefined;
|
|
|
|
let co2SignalEntity: string | undefined;
|
|
if (co2SignalConfigEntry) {
|
|
for (const entity of Object.values(hass.entities)) {
|
|
if (entity.platform !== "co2signal") {
|
|
continue;
|
|
}
|
|
|
|
// The integration offers 2 entities. We want the % one.
|
|
const co2State = hass.states[entity.entity_id];
|
|
if (!co2State || co2State.attributes.unit_of_measurement !== "%") {
|
|
continue;
|
|
}
|
|
|
|
co2SignalEntity = co2State.entity_id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const consumptionStatIDs: string[] = [];
|
|
for (const source of prefs.energy_sources) {
|
|
// grid source
|
|
if (source.type === "grid") {
|
|
for (const flowFrom of source.flow_from) {
|
|
consumptionStatIDs.push(flowFrom.stat_energy_from);
|
|
}
|
|
}
|
|
}
|
|
const energyStatIds = getReferencedStatisticIds(prefs, info, [
|
|
"grid",
|
|
"solar",
|
|
"battery",
|
|
"gas",
|
|
]);
|
|
const waterStatIds = getReferencedStatisticIds(prefs, info, ["water"]);
|
|
|
|
const allStatIDs = [...energyStatIds, ...waterStatIds];
|
|
|
|
const dayDifference = differenceInDays(end || new Date(), start);
|
|
const period =
|
|
dayDifference > 35 ? "month" : dayDifference > 2 ? "day" : "hour";
|
|
|
|
const lengthUnit = hass.config.unit_system.length || "";
|
|
const energyUnits: StatisticsUnitConfiguration = {
|
|
energy: "kWh",
|
|
volume: lengthUnit === "km" ? "m³" : "ft³",
|
|
};
|
|
const waterUnits: StatisticsUnitConfiguration = {
|
|
volume: lengthUnit === "km" ? "L" : "gal",
|
|
};
|
|
|
|
const _energyStats: Statistics | Promise<Statistics> = energyStatIds.length
|
|
? fetchStatistics(hass!, start, end, energyStatIds, period, energyUnits, [
|
|
"change",
|
|
])
|
|
: {};
|
|
const _waterStats: Statistics | Promise<Statistics> = waterStatIds.length
|
|
? fetchStatistics(hass!, start, end, waterStatIds, period, waterUnits, [
|
|
"change",
|
|
])
|
|
: {};
|
|
|
|
let statsCompare;
|
|
let startCompare;
|
|
let endCompare;
|
|
let _energyStatsCompare: Statistics | Promise<Statistics> = {};
|
|
let _waterStatsCompare: Statistics | Promise<Statistics> = {};
|
|
|
|
if (compare) {
|
|
if (dayDifference > 27 && dayDifference < 32) {
|
|
// When comparing a month, we want to start at the begining of the month
|
|
startCompare = addMonths(start, -1);
|
|
} else {
|
|
startCompare = addDays(start, (dayDifference + 1) * -1);
|
|
}
|
|
endCompare = addMilliseconds(start, -1);
|
|
if (energyStatIds.length) {
|
|
_energyStatsCompare = fetchStatistics(
|
|
hass!,
|
|
startCompare,
|
|
endCompare,
|
|
energyStatIds,
|
|
period,
|
|
energyUnits,
|
|
["change"]
|
|
);
|
|
}
|
|
if (waterStatIds.length) {
|
|
_waterStatsCompare = fetchStatistics(
|
|
hass!,
|
|
startCompare,
|
|
endCompare,
|
|
waterStatIds,
|
|
period,
|
|
waterUnits,
|
|
["change"]
|
|
);
|
|
}
|
|
}
|
|
|
|
let _fossilEnergyConsumption: undefined | Promise<FossilEnergyConsumption>;
|
|
let _fossilEnergyConsumptionCompare:
|
|
| undefined
|
|
| Promise<FossilEnergyConsumption>;
|
|
if (co2SignalEntity !== undefined) {
|
|
_fossilEnergyConsumption = getFossilEnergyConsumption(
|
|
hass!,
|
|
start,
|
|
consumptionStatIDs,
|
|
co2SignalEntity,
|
|
end,
|
|
dayDifference > 35 ? "month" : dayDifference > 2 ? "day" : "hour"
|
|
);
|
|
if (compare) {
|
|
_fossilEnergyConsumptionCompare = getFossilEnergyConsumption(
|
|
hass!,
|
|
startCompare,
|
|
consumptionStatIDs,
|
|
co2SignalEntity,
|
|
endCompare,
|
|
dayDifference > 35 ? "month" : dayDifference > 2 ? "day" : "hour"
|
|
);
|
|
}
|
|
}
|
|
|
|
const statsMetadata: Record<string, StatisticsMetaData> = {};
|
|
const _getStatisticMetadata:
|
|
| Promise<StatisticsMetaData[]>
|
|
| StatisticsMetaData[] = allStatIDs.length
|
|
? getStatisticMetadata(hass, allStatIDs)
|
|
: [];
|
|
const [
|
|
energyStats,
|
|
waterStats,
|
|
energyStatsCompare,
|
|
waterStatsCompare,
|
|
statsMetadataArray,
|
|
fossilEnergyConsumption,
|
|
fossilEnergyConsumptionCompare,
|
|
] = await Promise.all([
|
|
_energyStats,
|
|
_waterStats,
|
|
_energyStatsCompare,
|
|
_waterStatsCompare,
|
|
_getStatisticMetadata,
|
|
_fossilEnergyConsumption,
|
|
_fossilEnergyConsumptionCompare,
|
|
]);
|
|
const stats = { ...energyStats, ...waterStats };
|
|
if (compare) {
|
|
statsCompare = { ...energyStatsCompare, ...waterStatsCompare };
|
|
}
|
|
if (allStatIDs.length) {
|
|
statsMetadataArray.forEach((x) => {
|
|
statsMetadata[x.statistic_id] = x;
|
|
});
|
|
}
|
|
|
|
const data: EnergyData = {
|
|
start,
|
|
end,
|
|
startCompare,
|
|
endCompare,
|
|
info,
|
|
prefs,
|
|
stats,
|
|
statsMetadata,
|
|
statsCompare,
|
|
co2SignalConfigEntry,
|
|
co2SignalEntity,
|
|
fossilEnergyConsumption,
|
|
fossilEnergyConsumptionCompare,
|
|
};
|
|
|
|
return data;
|
|
};
|
|
|
|
export interface EnergyCollection extends Collection<EnergyData> {
|
|
start: Date;
|
|
end?: Date;
|
|
compare?: boolean;
|
|
prefs?: EnergyPreferences;
|
|
clearPrefs(): void;
|
|
setPeriod(newStart: Date, newEnd?: Date): void;
|
|
setCompare(compare: boolean): void;
|
|
_refreshTimeout?: number;
|
|
_updatePeriodTimeout?: number;
|
|
_active: number;
|
|
}
|
|
|
|
const clearEnergyCollectionPreferences = (hass: HomeAssistant) => {
|
|
energyCollectionKeys.forEach((key) => {
|
|
const energyCollection = getEnergyDataCollection(hass, { key });
|
|
energyCollection.clearPrefs();
|
|
if (energyCollection._active) {
|
|
energyCollection.refresh();
|
|
}
|
|
});
|
|
};
|
|
|
|
export const getEnergyDataCollection = (
|
|
hass: HomeAssistant,
|
|
options: { prefs?: EnergyPreferences; key?: string } = {}
|
|
): EnergyCollection => {
|
|
let key = "_energy";
|
|
if (options.key) {
|
|
if (!options.key.startsWith("energy_")) {
|
|
throw new Error("Key need to start with energy_");
|
|
}
|
|
key = `_${options.key}`;
|
|
}
|
|
|
|
if ((hass.connection as any)[key]) {
|
|
return (hass.connection as any)[key];
|
|
}
|
|
|
|
energyCollectionKeys.push(options.key);
|
|
|
|
const collection = getCollection<EnergyData>(
|
|
hass.connection,
|
|
key,
|
|
async () => {
|
|
if (!collection.prefs) {
|
|
// This will raise if not found.
|
|
// Detect by checking `e.code === "not_found"
|
|
collection.prefs = await getEnergyPreferences(hass);
|
|
}
|
|
|
|
if (collection._refreshTimeout) {
|
|
clearTimeout(collection._refreshTimeout);
|
|
}
|
|
|
|
if (
|
|
collection._active &&
|
|
(!collection.end || collection.end > new Date())
|
|
) {
|
|
// The stats are created every hour
|
|
// Schedule a refresh for 20 minutes past the hour
|
|
// If the end is larger than the current time.
|
|
const nextFetch = new Date();
|
|
if (nextFetch.getMinutes() >= 20) {
|
|
nextFetch.setHours(nextFetch.getHours() + 1);
|
|
}
|
|
nextFetch.setMinutes(20, 0, 0);
|
|
|
|
collection._refreshTimeout = window.setTimeout(
|
|
() => collection.refresh(),
|
|
nextFetch.getTime() - Date.now()
|
|
);
|
|
}
|
|
|
|
return getEnergyData(
|
|
hass,
|
|
collection.prefs,
|
|
collection.start,
|
|
collection.end,
|
|
collection.compare
|
|
);
|
|
}
|
|
) as EnergyCollection;
|
|
|
|
const origSubscribe = collection.subscribe;
|
|
|
|
collection.subscribe = (subscriber: (data: EnergyData) => void) => {
|
|
const unsub = origSubscribe(subscriber);
|
|
collection._active++;
|
|
return () => {
|
|
collection._active--;
|
|
if (collection._active < 1) {
|
|
clearTimeout(collection._refreshTimeout);
|
|
collection._refreshTimeout = undefined;
|
|
}
|
|
unsub();
|
|
};
|
|
};
|
|
|
|
collection._active = 0;
|
|
collection.prefs = options.prefs;
|
|
const now = new Date();
|
|
const hour = formatTime24h(now, hass.locale, hass.config).split(":")[0];
|
|
// Set start to start of today if we have data for today, otherwise yesterday
|
|
collection.start = calcDate(
|
|
hour === "0" ? addDays(now, -1) : now,
|
|
startOfDay,
|
|
hass.locale,
|
|
hass.config
|
|
);
|
|
collection.end = calcDate(
|
|
hour === "0" ? addDays(now, -1) : now,
|
|
endOfDay,
|
|
hass.locale,
|
|
hass.config
|
|
);
|
|
|
|
const scheduleUpdatePeriod = () => {
|
|
collection._updatePeriodTimeout = window.setTimeout(
|
|
() => {
|
|
collection.start = calcDate(
|
|
new Date(),
|
|
startOfDay,
|
|
hass.locale,
|
|
hass.config
|
|
);
|
|
collection.end = calcDate(
|
|
new Date(),
|
|
endOfDay,
|
|
hass.locale,
|
|
hass.config
|
|
);
|
|
scheduleUpdatePeriod();
|
|
},
|
|
addHours(calcDate(now, endOfDay, hass.locale, hass.config), 1).getTime() -
|
|
Date.now() // Switch to next day an hour after the day changed
|
|
);
|
|
};
|
|
scheduleUpdatePeriod();
|
|
|
|
collection.clearPrefs = () => {
|
|
collection.prefs = undefined;
|
|
};
|
|
collection.setPeriod = (newStart: Date, newEnd?: Date) => {
|
|
collection.start = newStart;
|
|
collection.end = newEnd;
|
|
if (
|
|
collection.start.getTime() ===
|
|
calcDate(new Date(), startOfDay, hass.locale, hass.config).getTime() &&
|
|
collection.end?.getTime() ===
|
|
calcDate(new Date(), endOfDay, hass.locale, hass.config).getTime() &&
|
|
!collection._updatePeriodTimeout
|
|
) {
|
|
scheduleUpdatePeriod();
|
|
} else if (collection._updatePeriodTimeout) {
|
|
clearTimeout(collection._updatePeriodTimeout);
|
|
collection._updatePeriodTimeout = undefined;
|
|
}
|
|
};
|
|
collection.setCompare = (compare: boolean) => {
|
|
collection.compare = compare;
|
|
};
|
|
return collection;
|
|
};
|
|
|
|
export const getEnergySolarForecasts = (hass: HomeAssistant) =>
|
|
hass.callWS<EnergySolarForecasts>({
|
|
type: "energy/solar_forecast",
|
|
});
|
|
|
|
const energyGasUnitClass = ["volume", "energy"] as const;
|
|
export type EnergyGasUnitClass = (typeof energyGasUnitClass)[number];
|
|
|
|
export const getEnergyGasUnitClass = (
|
|
prefs: EnergyPreferences,
|
|
statisticsMetaData: Record<string, StatisticsMetaData> = {},
|
|
excludeSource?: string
|
|
): EnergyGasUnitClass | undefined => {
|
|
for (const source of prefs.energy_sources) {
|
|
if (source.type !== "gas") {
|
|
continue;
|
|
}
|
|
if (excludeSource && excludeSource === source.stat_energy_from) {
|
|
continue;
|
|
}
|
|
const statisticIdWithMeta = statisticsMetaData[source.stat_energy_from];
|
|
if (
|
|
energyGasUnitClass.includes(
|
|
statisticIdWithMeta?.unit_class as EnergyGasUnitClass
|
|
)
|
|
) {
|
|
return statisticIdWithMeta.unit_class as EnergyGasUnitClass;
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
export const getEnergyGasUnit = (
|
|
hass: HomeAssistant,
|
|
prefs: EnergyPreferences,
|
|
statisticsMetaData: Record<string, StatisticsMetaData> = {}
|
|
): string | undefined => {
|
|
const unitClass = getEnergyGasUnitClass(prefs, statisticsMetaData);
|
|
if (unitClass === undefined) {
|
|
return undefined;
|
|
}
|
|
return unitClass === "energy"
|
|
? "kWh"
|
|
: hass.config.unit_system.length === "km"
|
|
? "m³"
|
|
: "ft³";
|
|
};
|
|
|
|
export const getEnergyWaterUnit = (hass: HomeAssistant): string | undefined =>
|
|
hass.config.unit_system.length === "km" ? "L" : "gal";
|