Download energy panel data to CSV (#19863)

* Download energy panel data to CSV

* table format changes

* unique types for cost/compensation
This commit is contained in:
karwosts 2024-02-27 23:02:39 -05:00 committed by GitHub
parent babb723521
commit a479c6e786
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 200 additions and 2 deletions

View File

@ -781,7 +781,7 @@ export const getEnergyGasUnit = (
: "ft³";
};
export const getEnergyWaterUnit = (hass: HomeAssistant): string | undefined =>
export const getEnergyWaterUnit = (hass: HomeAssistant): string =>
hass.config.unit_system.length === "km" ? "L" : "gal";
export const energyStatisticHelpUrl =

View File

@ -7,7 +7,7 @@ import {
html,
nothing,
} from "lit";
import { mdiPencil } from "@mdi/js";
import { mdiPencil, mdiDownload } from "@mdi/js";
import { customElement, property, state } from "lit/decorators";
import "../../components/ha-menu-button";
import "../../components/ha-list-item";
@ -19,6 +19,18 @@ import "../lovelace/components/hui-energy-period-selector";
import { Lovelace } from "../lovelace/types";
import "../lovelace/views/hui-view";
import { navigate } from "../../common/navigate";
import {
getEnergyDataCollection,
getEnergyGasUnit,
getEnergyWaterUnit,
GridSourceTypeEnergyPreference,
SolarSourceTypeEnergyPreference,
BatterySourceTypeEnergyPreference,
GasSourceTypeEnergyPreference,
WaterSourceTypeEnergyPreference,
DeviceConsumptionEnergyPreference,
} from "../../data/energy";
import { fileDownload } from "../../util/file_download";
const ENERGY_LOVELACE_CONFIG: LovelaceConfig = {
views: [
@ -86,6 +98,15 @@ class PanelEnergy extends LitElement {
</ha-svg-icon>
${this.hass!.localize("ui.panel.energy.configure")}
</ha-list-item>
<ha-list-item
slot="overflow-menu"
graphic="icon"
@request-selected=${this._dumpCSV}
>
<ha-svg-icon slot="graphic" .path=${mdiDownload}>
</ha-svg-icon>
${this.hass!.localize("ui.panel.energy.download_data")}
</ha-list-item>
`
: nothing}
</hui-energy-period-selector>
@ -122,6 +143,182 @@ class PanelEnergy extends LitElement {
navigate("/config/energy?historyBack=1");
}
private async _dumpCSV(ev) {
ev.stopPropagation();
const energyData = getEnergyDataCollection(this.hass, {
key: "energy_dashboard",
});
if (!energyData.prefs || !energyData.state.stats) {
return;
}
const gasUnit =
getEnergyGasUnit(
this.hass,
energyData.prefs,
energyData.state.statsMetadata
) || "";
const waterUnit = getEnergyWaterUnit(this.hass);
const electricUnit = "kWh";
const energy_sources = energyData.prefs.energy_sources;
const device_consumption = energyData.prefs.device_consumption;
const stats = energyData.state.stats;
const timeSet = new Set<number>();
Object.values(stats).forEach((stat) => {
stat.forEach((datapoint) => {
timeSet.add(datapoint.start);
});
});
const times = Array.from(timeSet).sort();
const headers =
"entity_id,type,unit," +
times.map((t) => new Date(t).toISOString()).join(",") +
"\n";
const csv: string[] = [];
csv[0] = headers;
const processStat = function (stat: string, type: string, unit: string) {
let n = 0;
const row: string[] = [];
if (!stats[stat]) {
return;
}
row.push(stat);
row.push(type);
row.push(unit.normalize("NFKD"));
times.forEach((t) => {
if (stats[stat][n].start > t) {
row.push("");
} else if (n < stats[stat].length && stats[stat][n].start === t) {
row.push((stats[stat][n].change ?? "").toString());
n++;
} else {
row.push("");
}
});
csv.push(row.join(",") + "\n");
};
const currency = this.hass.config.currency;
const printCategory = function (
type: string,
statIds: string[],
unit: string,
costType?: string
) {
if (statIds.length) {
statIds.forEach((stat) => processStat(stat, type, unit));
if (costType) {
statIds.forEach((stat) => {
const costStat = energyData.state.info.cost_sensors[stat];
if (energyData.state.info.cost_sensors[stat]) {
processStat(costStat, costType, currency);
}
});
}
}
};
const grid_consumptions: string[] = [];
const grid_productions: string[] = [];
energy_sources
.filter((s) => s.type === "grid")
.forEach((source) => {
source = source as GridSourceTypeEnergyPreference;
source.flow_from.forEach((flowFrom) => {
grid_consumptions.push(flowFrom.stat_energy_from);
});
source.flow_to.forEach((flowTo) => {
grid_productions.push(flowTo.stat_energy_to);
});
});
printCategory(
"grid_consumption",
grid_consumptions,
electricUnit,
"grid_consumption_cost"
);
printCategory(
"grid_return",
grid_productions,
electricUnit,
"grid_return_compensation"
);
const battery_ins: string[] = [];
const battery_outs: string[] = [];
energy_sources
.filter((s) => s.type === "battery")
.forEach((source) => {
source = source as BatterySourceTypeEnergyPreference;
battery_ins.push(source.stat_energy_to);
battery_outs.push(source.stat_energy_from);
});
printCategory("battery_in", battery_ins, electricUnit);
printCategory("battery_out", battery_outs, electricUnit);
const solar_productions: string[] = [];
energy_sources
.filter((s) => s.type === "solar")
.forEach((source) => {
source = source as SolarSourceTypeEnergyPreference;
solar_productions.push(source.stat_energy_from);
});
printCategory("solar_production", solar_productions, electricUnit);
const gas_consumptions: string[] = [];
energy_sources
.filter((s) => s.type === "gas")
.forEach((source) => {
source = source as GasSourceTypeEnergyPreference;
gas_consumptions.push(source.stat_energy_from);
});
printCategory(
"gas_consumption",
gas_consumptions,
gasUnit,
"gas_consumption_cost"
);
const water_consumptions: string[] = [];
energy_sources
.filter((s) => s.type === "water")
.forEach((source) => {
source = source as WaterSourceTypeEnergyPreference;
water_consumptions.push(source.stat_energy_from);
});
printCategory(
"water_consumption",
water_consumptions,
waterUnit,
"water_consumption_cost"
);
const devices: string[] = [];
device_consumption.forEach((source) => {
source = source as DeviceConsumptionEnergyPreference;
devices.push(source.stat_consumption);
});
printCategory("device_consumption", devices, electricUnit);
const blob = new Blob(csv, {
type: "text/csv",
});
const url = window.URL.createObjectURL(blob);
fileDownload(url, "energy.csv");
}
private _reloadView() {
// Force strategy to be re-run by make a copy of the view
const config = this._lovelace!.config;

View File

@ -6538,6 +6538,7 @@
}
},
"energy": {
"download_data": "[%key:ui::panel::history::download_data%]",
"configure": "[%key:ui::dialogs::quick-bar::commands::navigation::energy%]",
"compare": {
"info": "You are comparing the period {start} with the period {end}"