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:
parent
babb723521
commit
a479c6e786
|
@ -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 =
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}"
|
||||
|
|
Loading…
Reference in New Issue