Files
Symcon_Belevo_Energiemanage…/PV_Forecast/module.html
2026-01-20 09:50:47 +01:00

111 lines
3.4 KiB
HTML

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>PV_Forecast</title>
<!-- Hinweis: Wenn Symcon/CDN blockiert, kommt "Highcharts nicht geladen".
Dann sag Bescheid, dann liefern wir Highcharts lokal aus. -->
<script src="https://code.highcharts.com/highcharts.js"></script>
<style>
body { font-family: sans-serif; margin: 0; padding: 12px; }
#chart { height: 420px; width: 100%; }
.row { display:flex; gap:12px; align-items:center; margin-bottom:10px; flex-wrap:wrap; }
.badge { padding:4px 8px; border-radius:10px; background:#eee; }
button { padding:6px 10px; cursor:pointer; }
</style>
</head>
<body>
<div class="row">
<button id="reload">Neu laden</button>
<span class="badge" id="meta"></span>
</div>
<div id="chart"></div>
<script>
const metaEl = document.getElementById("meta");
window.onerror = function (msg, url, line, col) {
metaEl.textContent = `JS-Fehler: ${msg} (${line}:${col})`;
};
// VSCode-freundlich (Platzhalter als String)
window.instanceId = Number("{{INSTANCE_ID}}");
const instanceId = window.instanceId;
if (!Number.isFinite(instanceId) || instanceId <= 0) {
metaEl.textContent = "Fehler: INSTANCE_ID wurde nicht korrekt eingesetzt.";
throw new Error("Invalid INSTANCE_ID");
}
const endpoint =
`${location.protocol}//${location.host}` +
`/hook/solcastcompare?instance=${instanceId}&action=data`;
let chart;
async function loadData() {
metaEl.textContent = "Lade Daten…";
const r = await fetch(endpoint, { cache: "no-store" });
if (!r.ok) {
const text = await r.text().catch(() => "");
throw new Error(`HTTP ${r.status}${text ? " - " + text : ""}`);
}
return await r.json();
}
function render(data) {
const cachedAt = data?.meta?.forecast_cached_at
? new Date(data.meta.forecast_cached_at * 1000).toLocaleString()
: "unbekannt";
const forecast = data?.series?.forecast ?? [];
const actual = data?.series?.actual ?? [];
const isWatt = !!data?.meta?.actual_is_watt;
if (typeof Highcharts === "undefined") {
metaEl.textContent = "Fehler: Highcharts nicht geladen (CSP/CDN/Mixed Content).";
return;
}
const yLabel = isWatt ? "Leistung (kW) [Ist umgerechnet]" : "Leistung (kW)";
if (!chart) {
chart = Highcharts.chart("chart", {
title: { text: "PV: Erwartung vs. Tatsächlich" },
xAxis: { type: "datetime" },
yAxis: { title: { text: yLabel } },
tooltip: { shared: true, xDateFormat: "%d.%m.%Y %H:%M" },
legend: { enabled: true },
series: [
{ name: "Erwartet (Solcast)", data: forecast },
{ name: "Tatsächlich (Archiv)", data: actual }
]
});
} else {
chart.yAxis[0].setTitle({ text: yLabel }, false);
chart.series[0].setData(forecast, false);
chart.series[1].setData(actual, false);
chart.redraw();
}
metaEl.textContent = `OK | Cache: ${cachedAt} | Forecast: ${forecast.length} | Ist: ${actual.length}`;
}
async function refresh() {
try {
const data = await loadData();
render(data);
} catch (e) {
metaEl.textContent = "Fehler beim Laden: " + (e?.message ?? e);
}
}
document.getElementById("reload").addEventListener("click", refresh);
refresh();
</script>
</body>
</html>