From ac21468f0ece6366820ef5d8985eea5665f8ea2d Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sat, 8 Aug 2026 15:31:24 +0200 Subject: [PATCH 01/48] Add forecast configuration UI with provider forms and manual refresh --- src/components/OpenwbPageNavbar.vue | 8 + .../forecast/ForecastConfigMixin.vue | 13 ++ .../forecast/OpenwbForecastConfigFallback.vue | 36 ++++ .../forecast/OpenwbForecastProxy.vue | 44 +++++ .../forecast/forecastsolar/forecast.vue | 58 ++++++ .../forecast/openmeteo/forecast.vue | 63 +++++++ src/components/forecast/pvnode/forecast.vue | 46 +++++ src/router/index.js | 9 + src/views/ForecastConfiguration.vue | 176 ++++++++++++++++++ 9 files changed, 453 insertions(+) create mode 100644 src/components/forecast/ForecastConfigMixin.vue create mode 100644 src/components/forecast/OpenwbForecastConfigFallback.vue create mode 100644 src/components/forecast/OpenwbForecastProxy.vue create mode 100644 src/components/forecast/forecastsolar/forecast.vue create mode 100644 src/components/forecast/openmeteo/forecast.vue create mode 100644 src/components/forecast/pvnode/forecast.vue create mode 100644 src/views/ForecastConfiguration.vue diff --git a/src/components/OpenwbPageNavbar.vue b/src/components/OpenwbPageNavbar.vue index 175d0b22d..eafc13257 100644 --- a/src/components/OpenwbPageNavbar.vue +++ b/src/components/OpenwbPageNavbar.vue @@ -175,6 +175,14 @@ > Lastmanagement + + PV-Prognose + +export default { + props: { + forecast: { type: Object, required: true }, + }, + emits: ["update:configuration"], + methods: { + updateConfiguration(event, path = undefined) { + this.$emit("update:configuration", { value: event, object: path }); + }, + }, +}; + diff --git a/src/components/forecast/OpenwbForecastConfigFallback.vue b/src/components/forecast/OpenwbForecastConfigFallback.vue new file mode 100644 index 000000000..ad2a48428 --- /dev/null +++ b/src/components/forecast/OpenwbForecastConfigFallback.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/components/forecast/OpenwbForecastProxy.vue b/src/components/forecast/OpenwbForecastProxy.vue new file mode 100644 index 000000000..254bba0e9 --- /dev/null +++ b/src/components/forecast/OpenwbForecastProxy.vue @@ -0,0 +1,44 @@ + + + diff --git a/src/components/forecast/forecastsolar/forecast.vue b/src/components/forecast/forecastsolar/forecast.vue new file mode 100644 index 000000000..0c43a5c47 --- /dev/null +++ b/src/components/forecast/forecastsolar/forecast.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/components/forecast/openmeteo/forecast.vue b/src/components/forecast/openmeteo/forecast.vue new file mode 100644 index 000000000..67d7f70a2 --- /dev/null +++ b/src/components/forecast/openmeteo/forecast.vue @@ -0,0 +1,63 @@ + + + diff --git a/src/components/forecast/pvnode/forecast.vue b/src/components/forecast/pvnode/forecast.vue new file mode 100644 index 000000000..fcb4afc17 --- /dev/null +++ b/src/components/forecast/pvnode/forecast.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/router/index.js b/src/router/index.js index 89f33ac7e..d763945a8 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -105,6 +105,15 @@ const routes = [ }, component: () => import("../views/LoadManagementConfiguration.vue"), }, + { + path: "/ForecastConfiguration", + name: "ForecastConfiguration", + meta: { + heading: "Konfiguration - PV-Prognose", + checkPermissions: false, + }, + component: () => import("../views/ForecastConfiguration.vue"), + }, { path: "/ChargePointInstallation", name: "ChargePointInstallation", diff --git a/src/views/ForecastConfiguration.vue b/src/views/ForecastConfiguration.vue new file mode 100644 index 000000000..a36651ed3 --- /dev/null +++ b/src/views/ForecastConfiguration.vue @@ -0,0 +1,176 @@ + + + From 1d2c33a3e0e59a168469947dfd928c81957a64bf Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sat, 8 Aug 2026 15:35:13 +0200 Subject: [PATCH 02/48] Improve forecast forms with orientation hints and multi-array inputs --- .../forecast/forecastsolar/forecast.vue | 43 +++++++++++---- .../forecast/openmeteo/forecast.vue | 54 +++++++++++++++++-- src/views/ForecastConfiguration.vue | 16 +++--- 3 files changed, 93 insertions(+), 20 deletions(-) diff --git a/src/components/forecast/forecastsolar/forecast.vue b/src/components/forecast/forecastsolar/forecast.vue index 0c43a5c47..5aa3ff7d6 100644 --- a/src/components/forecast/forecastsolar/forecast.vue +++ b/src/components/forecast/forecastsolar/forecast.vue @@ -1,5 +1,9 @@ diff --git a/src/components/forecast/openmeteo/forecast.vue b/src/components/forecast/openmeteo/forecast.vue index 67d7f70a2..a5bab6b26 100644 --- a/src/components/forecast/openmeteo/forecast.vue +++ b/src/components/forecast/openmeteo/forecast.vue @@ -1,5 +1,9 @@ + + + + + + + + + - + + > + + diff --git a/src/views/ForecastConfiguration.vue b/src/views/ForecastConfiguration.vue index a36651ed3..1f8740abc 100644 --- a/src/views/ForecastConfiguration.vue +++ b/src/views/ForecastConfiguration.vue @@ -72,9 +72,12 @@ const PROVIDER_DEFINITIONS = { latitude: 0, longitude: 0, timezone: "Europe/Berlin", - peak_power_kw: 0, - system_loss: 0, - irradiance_to_power_factor: 1, + peak_power_kw: 9.5, + tilt: 30, + azimuth: 0, + system_loss: 0.14, + irradiance_to_power_factor: 0.2, + strings: [], }, }, forecastsolar: { @@ -84,9 +87,10 @@ const PROVIDER_DEFINITIONS = { configuration: { latitude: 0, longitude: 0, - peak_power_kw: 0, + peak_power_kw: 9.5, azimuth: 0, tilt: 30, + strings: [], }, }, pvnode: { @@ -96,8 +100,8 @@ const PROVIDER_DEFINITIONS = { configuration: { api_key: "", plant_id: "", - peak_power_kw: 0, - system_loss: 0, + peak_power_kw: 9.5, + system_loss: 0.14, }, }, }; From 063af2e49e3f251c008f85d16f972d7f89ebbd77 Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sat, 8 Aug 2026 15:38:40 +0200 Subject: [PATCH 03/48] Simplify PVNode UI to API key and plant ID only --- src/components/forecast/pvnode/forecast.vue | 24 ++++----------------- src/views/ForecastConfiguration.vue | 2 -- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/components/forecast/pvnode/forecast.vue b/src/components/forecast/pvnode/forecast.vue index fcb4afc17..062576d28 100644 --- a/src/components/forecast/pvnode/forecast.vue +++ b/src/components/forecast/pvnode/forecast.vue @@ -1,5 +1,9 @@ diff --git a/src/views/ForecastConfiguration.vue b/src/views/ForecastConfiguration.vue index 1f8740abc..97f086fb3 100644 --- a/src/views/ForecastConfiguration.vue +++ b/src/views/ForecastConfiguration.vue @@ -100,8 +100,6 @@ const PROVIDER_DEFINITIONS = { configuration: { api_key: "", plant_id: "", - peak_power_kw: 9.5, - system_loss: 0.14, }, }, }; From 0060cedb0b3f97c86148e706b30d44775fa53435 Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sat, 8 Aug 2026 15:41:51 +0200 Subject: [PATCH 04/48] Replace forecast JSON strings with row-based orientation editor --- .../forecast/forecastsolar/forecast.vue | 108 ++++++++++++++++-- .../forecast/openmeteo/forecast.vue | 108 ++++++++++++++++-- 2 files changed, 196 insertions(+), 20 deletions(-) diff --git a/src/components/forecast/forecastsolar/forecast.vue b/src/components/forecast/forecastsolar/forecast.vue index 5aa3ff7d6..f723b70df 100644 --- a/src/components/forecast/forecastsolar/forecast.vue +++ b/src/components/forecast/forecastsolar/forecast.vue @@ -43,17 +43,68 @@ Wird verwendet, wenn keine Mehrfach-Ausrichtungen konfiguriert sind. Typischer Bereich: 5 - 15 kWp. - + Mehrere Ausrichtungen: Falls mindestens ein Eintrag vorhanden ist, werden diese Einzelflaechen verwendet und die + Summenwerte oben ignoriert. + + + - - + Maximal 6 Ausrichtungen sind erlaubt. + + + Keine zusaetzlichen Ausrichtungen konfiguriert. + + + + + + + = 6) { + return; + } + const next = [ + ...this.stringRows, + { + peak_power_kw: this.forecast.configuration.peak_power_kw || 1, + tilt: this.forecast.configuration.tilt ?? 30, + azimuth: this.forecast.configuration.azimuth ?? 0, + }, + ]; + this.updateConfiguration(next, "configuration.strings"); + }, + removeStringRow(index) { + const next = this.stringRows.filter((_, rowIndex) => rowIndex !== index); + this.updateConfiguration(next, "configuration.strings"); + }, + updateStringField(index, key, value) { + const next = this.stringRows.map((row, rowIndex) => { + if (rowIndex !== index) { + return row; + } + return { + ...row, + [key]: value, + }; + }); + this.updateConfiguration(next, "configuration.strings"); + }, + }, }; diff --git a/src/components/forecast/openmeteo/forecast.vue b/src/components/forecast/openmeteo/forecast.vue index a5bab6b26..63c7be558 100644 --- a/src/components/forecast/openmeteo/forecast.vue +++ b/src/components/forecast/openmeteo/forecast.vue @@ -61,17 +61,68 @@ > - + Mehrere Ausrichtungen: Falls mindestens ein Eintrag vorhanden ist, werden diese Einzelflaechen verwendet und die + Summenwerte oben ignoriert. + + + - - + Maximal 6 Ausrichtungen sind erlaubt. + + + Keine zusaetzlichen Ausrichtungen konfiguriert. + + + + + + + = 6) { + return; + } + const next = [ + ...this.stringRows, + { + peak_power_kw: this.forecast.configuration.peak_power_kw || 1, + tilt: this.forecast.configuration.tilt ?? 30, + azimuth: this.forecast.configuration.azimuth ?? 0, + }, + ]; + this.updateConfiguration(next, "configuration.strings"); + }, + removeStringRow(index) { + const next = this.stringRows.filter((_, rowIndex) => rowIndex !== index); + this.updateConfiguration(next, "configuration.strings"); + }, + updateStringField(index, key, value) { + const next = this.stringRows.map((row, rowIndex) => { + if (rowIndex !== index) { + return row; + } + return { + ...row, + [key]: value, + }; + }); + this.updateConfiguration(next, "configuration.strings"); + }, + }, }; From 15090817f5c0557f2e60d185196535cb19d5f371 Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sat, 8 Aug 2026 16:45:16 +0200 Subject: [PATCH 05/48] fix(ui): replace wildcard security access subscriptions --- src/App.vue | 30 ++++++++++++++++++++++++++++- src/components/OpenwbPageNavbar.vue | 29 +++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/App.vue b/src/App.vue index db18df6ca..0d034c9ed 100644 --- a/src/App.vue +++ b/src/App.vue @@ -64,6 +64,34 @@ export default { requestProblemInformation: true, }, }, + securityAccessTopics: [ + "openWB/system/security/access/Settings", + "openWB/system/security/access/Status", + "openWB/system/security/access/ChargeLog", + "openWB/system/security/access/Chart", + "openWB/system/security/access/GeneralConfiguration", + "openWB/system/security/access/DisplayConfiguration", + "openWB/system/security/access/IdentificationConfiguration", + "openWB/system/security/access/GeneralChargeConfiguration", + "openWB/system/security/access/SurplusChargeConfiguration", + "openWB/system/security/access/ActiveBatControlConfiguration", + "openWB/system/security/access/HardwareInstallation", + "openWB/system/security/access/LoadManagementConfiguration", + "openWB/system/security/access/ChargePointInstallation", + "openWB/system/security/access/VehicleConfiguration", + "openWB/system/security/access/IoConfiguration", + "openWB/system/security/access/LegacySmartHomeConfiguration", + "openWB/system/security/access/InstallAssistant", + "openWB/system/security/access/TenantEnergyConfiguration", + "openWB/system/security/access/CloudConfiguration", + "openWB/system/security/access/MqttBridgeConfiguration", + "openWB/system/security/access/DebugConfiguration", + "openWB/system/security/access/Support", + "openWB/system/security/access/DataManagement", + "openWB/system/security/access/SecurityConfiguration", + "openWB/system/security/access/SystemConfiguration", + "openWB/system/security/access/LegalSettings", + ], dataTimeout: null, }; }, @@ -256,7 +284,7 @@ export default { "openWB/system/dataprotection_acknowledged", "openWB/system/usage_terms_acknowledged", "openWB/system/installAssistantDone", - "openWB/system/security/access/+", + ...this.securityAccessTopics, ], true, ); diff --git a/src/components/OpenwbPageNavbar.vue b/src/components/OpenwbPageNavbar.vue index eafc13257..65d2e543d 100644 --- a/src/components/OpenwbPageNavbar.vue +++ b/src/components/OpenwbPageNavbar.vue @@ -376,7 +376,34 @@ export default { mixins: [ComponentState], data() { return { - mqttTopics: [{ topic: "openWB/system/security/access/+", writeable: false }], + mqttTopics: [ + { topic: "openWB/system/security/access/Settings", writeable: false }, + { topic: "openWB/system/security/access/Status", writeable: false }, + { topic: "openWB/system/security/access/ChargeLog", writeable: false }, + { topic: "openWB/system/security/access/Chart", writeable: false }, + { topic: "openWB/system/security/access/GeneralConfiguration", writeable: false }, + { topic: "openWB/system/security/access/DisplayConfiguration", writeable: false }, + { topic: "openWB/system/security/access/IdentificationConfiguration", writeable: false }, + { topic: "openWB/system/security/access/GeneralChargeConfiguration", writeable: false }, + { topic: "openWB/system/security/access/SurplusChargeConfiguration", writeable: false }, + { topic: "openWB/system/security/access/ActiveBatControlConfiguration", writeable: false }, + { topic: "openWB/system/security/access/HardwareInstallation", writeable: false }, + { topic: "openWB/system/security/access/LoadManagementConfiguration", writeable: false }, + { topic: "openWB/system/security/access/ChargePointInstallation", writeable: false }, + { topic: "openWB/system/security/access/VehicleConfiguration", writeable: false }, + { topic: "openWB/system/security/access/IoConfiguration", writeable: false }, + { topic: "openWB/system/security/access/LegacySmartHomeConfiguration", writeable: false }, + { topic: "openWB/system/security/access/InstallAssistant", writeable: false }, + { topic: "openWB/system/security/access/TenantEnergyConfiguration", writeable: false }, + { topic: "openWB/system/security/access/CloudConfiguration", writeable: false }, + { topic: "openWB/system/security/access/MqttBridgeConfiguration", writeable: false }, + { topic: "openWB/system/security/access/DebugConfiguration", writeable: false }, + { topic: "openWB/system/security/access/Support", writeable: false }, + { topic: "openWB/system/security/access/DataManagement", writeable: false }, + { topic: "openWB/system/security/access/SecurityConfiguration", writeable: false }, + { topic: "openWB/system/security/access/SystemConfiguration", writeable: false }, + { topic: "openWB/system/security/access/LegalSettings", writeable: false }, + ], }; }, computed: { From 60718b4c7a640e77a72d6df9b0a63a9b50550981 Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sat, 8 Aug 2026 17:01:28 +0200 Subject: [PATCH 06/48] fix(ui): normalize forecast provider topic for editable config --- src/views/ForecastConfiguration.vue | 173 ++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 9 deletions(-) diff --git a/src/views/ForecastConfiguration.vue b/src/views/ForecastConfiguration.vue index 97f086fb3..3d1e8bfd6 100644 --- a/src/views/ForecastConfiguration.vue +++ b/src/views/ForecastConfiguration.vue @@ -42,6 +42,16 @@ {{ formatNumber($store.state.mqtt["openWB/optional/forecast/get/tomorrow_kwh"], 2, 2) || "0.00" }} kWh + + + + + Noch keine Forecast-Werte vorhanden. Fuehre ggf. "Forecast Jetzt Aktualisieren" aus. + +
+ +
+
+ import ComponentState from "../components/mixins/ComponentState.vue"; import OpenwbForecastProxy from "../components/forecast/OpenwbForecastProxy.vue"; +import { Line as ChartjsLine } from "vue-chartjs"; +import "chartjs-adapter-luxon"; +import { + Chart, + Tooltip, + Legend, + LineController, + LineElement, + PointElement, + LinearScale, + TimeScale, + Filler, +} from "chart.js"; + +Chart.register(Tooltip, Legend, LineController, LineElement, PointElement, LinearScale, TimeScale, Filler); const PROVIDER_DEFINITIONS = { openmeteo: { @@ -108,6 +151,7 @@ export default { name: "OpenwbForecastConfiguration", components: { OpenwbForecastProxy, + ChartjsLine, }, mixins: [ComponentState], emits: ["save", "reset"], @@ -137,34 +181,139 @@ export default { { value: "pvnode", text: "PVNode" }, ]; }, + currentForecastProviderRaw() { + return this.$store.state.mqtt["openWB/optional/forecast/provider"]; + }, currentForecastProvider() { - const provider = this.$store.state.mqtt["openWB/optional/forecast/provider"]; + const provider = this.currentForecastProviderRaw; if (provider && typeof provider === "object") { return provider; } + if (typeof provider === "string" && PROVIDER_DEFINITIONS[provider]) { + return this.createProviderByType(provider); + } return { type: null, configuration: {} }; }, selectedProviderType() { + if (typeof this.currentForecastProviderRaw === "string") { + return this.currentForecastProviderRaw; + } return this.currentForecastProvider.type || ""; }, + forecastValues() { + const values = this.$store.state.mqtt["openWB/optional/forecast/get/values"]; + return values && typeof values === "object" ? values : {}; + }, + hasForecastValues() { + return Object.keys(this.forecastValues).length > 0; + }, + forecastChartData() { + const points = Object.entries(this.forecastValues) + .map(([timestamp, value]) => ({ + x: Number(timestamp) * 1000, + y: Number(value) / 1000, + })) + .filter((item) => Number.isFinite(item.x) && Number.isFinite(item.y)) + .sort((a, b) => a.x - b.x); + + return { + datasets: [ + { + label: "Forecast Leistung", + borderColor: "#28a745", + backgroundColor: "rgba(40, 167, 69, 0.2)", + fill: true, + pointRadius: 0, + pointHoverRadius: 3, + borderWidth: 2, + tension: 0.2, + data: points, + }, + ], + }; + }, + forecastChartOptions() { + return { + responsive: true, + maintainAspectRatio: false, + parsing: false, + plugins: { + legend: { + display: true, + }, + }, + scales: { + x: { + type: "time", + time: { + tooltipFormat: "dd.MM.yyyy HH:mm", + displayFormats: { + hour: "dd.MM HH:mm", + }, + }, + title: { + display: true, + text: "Zeit", + }, + }, + y: { + title: { + display: true, + text: "Leistung (kW)", + }, + }, + }, + }; + }, + nextQueryTimeText() { + const timestamp = this.$store.state.mqtt["openWB/optional/forecast/get/next_query_time"]; + if (!timestamp) { + return "-"; + } + return new Date(Number(timestamp) * 1000).toLocaleString(); + }, + faultStatusText() { + const faultText = this.$store.state.mqtt["openWB/optional/forecast/get/fault_str"]; + return faultText && faultText.length > 0 ? faultText : "OK"; + }, + }, + watch: { + currentForecastProviderRaw() { + this.normalizeProviderTopic(); + }, + }, + mounted() { + this.normalizeProviderTopic(); }, methods: { - updateProviderType(type) { - if (!type) { - this.updateState("openWB/optional/forecast/provider", { type: null, configuration: {} }); - return; - } + createProviderByType(type, configuration = undefined) { const definition = PROVIDER_DEFINITIONS[type]; - const existing = this.currentForecastProvider; - const nextProvider = { + if (!definition) { + return { type: null, configuration: {} }; + } + return { name: definition.name, type: definition.type, official: definition.official, configuration: { ...definition.configuration, - ...(existing.type === type ? existing.configuration : {}), + ...(configuration && typeof configuration === "object" ? configuration : {}), }, }; + }, + normalizeProviderTopic() { + const provider = this.currentForecastProviderRaw; + if (typeof provider === "string" && PROVIDER_DEFINITIONS[provider]) { + this.updateState("openWB/optional/forecast/provider", this.createProviderByType(provider)); + } + }, + updateProviderType(type) { + if (!type) { + this.updateState("openWB/optional/forecast/provider", { type: null, configuration: {} }); + return; + } + const existing = this.currentForecastProvider; + const nextProvider = this.createProviderByType(type, existing.type === type ? existing.configuration : {}); this.updateState("openWB/optional/forecast/provider", nextProvider); }, updateConfiguration(topic, event) { @@ -176,3 +325,9 @@ export default { }, }; + + From 7d9abc1a24463cfadbac6cf0613bc3e114da805e Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sat, 8 Aug 2026 17:21:20 +0200 Subject: [PATCH 07/48] feat(ui): improve forecast provider forms and workflow --- .../forecast/forecastsolar/forecast.vue | 58 ++++------ .../forecast/openmeteo/forecast.vue | 106 ++++++++---------- src/components/forecast/pvnode/forecast.vue | 4 +- src/views/ForecastConfiguration.vue | 58 ++++++++-- 4 files changed, 119 insertions(+), 107 deletions(-) diff --git a/src/components/forecast/forecastsolar/forecast.vue b/src/components/forecast/forecastsolar/forecast.vue index f723b70df..30e372f01 100644 --- a/src/components/forecast/forecastsolar/forecast.vue +++ b/src/components/forecast/forecastsolar/forecast.vue @@ -1,37 +1,28 @@ @@ -139,6 +124,7 @@ export default { const next = [ ...this.stringRows, { + name: `Ausrichtung ${this.stringRows.length + 1}`, peak_power_kw: this.forecast.configuration.peak_power_kw || 1, tilt: this.forecast.configuration.tilt ?? 30, azimuth: this.forecast.configuration.azimuth ?? 0, diff --git a/src/components/forecast/openmeteo/forecast.vue b/src/components/forecast/openmeteo/forecast.vue index 63c7be558..bb49ff4e7 100644 --- a/src/components/forecast/openmeteo/forecast.vue +++ b/src/components/forecast/openmeteo/forecast.vue @@ -1,69 +1,75 @@ @@ -171,6 +158,7 @@ export default { const next = [ ...this.stringRows, { + name: `Ausrichtung ${this.stringRows.length + 1}`, peak_power_kw: this.forecast.configuration.peak_power_kw || 1, tilt: this.forecast.configuration.tilt ?? 30, azimuth: this.forecast.configuration.azimuth ?? 0, diff --git a/src/components/forecast/pvnode/forecast.vue b/src/components/forecast/pvnode/forecast.vue index 062576d28..825de5fe2 100644 --- a/src/components/forecast/pvnode/forecast.vue +++ b/src/components/forecast/pvnode/forecast.vue @@ -1,8 +1,8 @@ From fd0dc0e78761880a958704a88f56d229e0cb408c Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sun, 9 Aug 2026 09:56:59 +0200 Subject: [PATCH 34/48] fix(store): guard against undefined state.examples in production build --- src/store/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/index.js b/src/store/index.js index f712c6f3b..0875fdb1f 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -126,7 +126,7 @@ export default createStore({ const setPath = (object, path, value) => path.split(".").reduce((o, p, i) => (o[p] = path.split(".").length === i + 1 ? value : o[p] || {}), object); - if (message.topic in state.mqtt || !(message.topic in state.examples)) { + if (message.topic in state.mqtt || !state.examples || !(message.topic in state.examples)) { if (message.objectPath != undefined) { setPath(state.mqtt[message.topic], message.objectPath, message.payload); } else { From 804818a27a8d2ddd91f7a77d3b8437fe53ed90a5 Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sun, 9 Aug 2026 10:13:04 +0200 Subject: [PATCH 35/48] =?UTF-8?q?feat(forecast-ui):=20rename=20to=20Dachfl?= =?UTF-8?q?=C3=A4che,=20move=20add-button=20below,=20permanent=20azimuth?= =?UTF-8?q?=20hint,=20smaller=20buttons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../forecast/forecastsolar/forecast.vue | 35 +++++++++---------- .../forecast/openmeteo/forecast.vue | 33 ++++++++--------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/components/forecast/forecastsolar/forecast.vue b/src/components/forecast/forecastsolar/forecast.vue index 4747c5451..cfa3ddab8 100644 --- a/src/components/forecast/forecastsolar/forecast.vue +++ b/src/components/forecast/forecastsolar/forecast.vue @@ -22,38 +22,33 @@
- Jede Ausrichtung wird einzeln angelegt. Auch bei nur einer Dachflaeche bitte eine Ausrichtung erfassen. + Jede Dachflaeche muss einzeln erfasst werden, auch wenn es nur eine Dachflaeche gibt. - - Maximal 6 Ausrichtungen sind erlaubt. + Maximal 6 Dachflaechen sind erlaubt. - Keine zusaetzlichen Ausrichtungen konfiguriert. + Noch keine Dachflaechen konfiguriert. - - + /> +

+ Azimut: 0° = Sueden • -90° = Osten • 90° = Westen • 180° = Norden +

+ diff --git a/src/components/forecast/openmeteo/forecast.vue b/src/components/forecast/openmeteo/forecast.vue index 66118ba3f..116bfe7ea 100644 --- a/src/components/forecast/openmeteo/forecast.vue +++ b/src/components/forecast/openmeteo/forecast.vue @@ -45,38 +45,33 @@ - Jede Ausrichtung wird einzeln angelegt. Auch bei nur einer Dachflaeche bitte eine Ausrichtung erfassen. + Jede Dachflaeche muss einzeln erfasst werden, auch wenn es nur eine Dachflaeche gibt. - - Maximal 6 Ausrichtungen sind erlaubt. + Maximal 6 Dachflaechen sind erlaubt. - Keine zusaetzlichen Ausrichtungen konfiguriert. + Noch keine Dachflaechen konfiguriert. - - + /> +

+ Azimut: 0° = Sueden • -90° = Osten • 90° = Westen • 180° = Norden +

+ From 7f9191289bfa082f18597161981a429f339ac797 Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sun, 9 Aug 2026 10:22:34 +0200 Subject: [PATCH 36/48] feat(forecast-ui): rename cards, day filter, hover tooltips, bigger chart, last update time --- src/views/ForecastConfiguration.vue | 73 +++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/src/views/ForecastConfiguration.vue b/src/views/ForecastConfiguration.vue index 4dcbf7cfe..3d51d543c 100644 --- a/src/views/ForecastConfiguration.vue +++ b/src/views/ForecastConfiguration.vue @@ -50,13 +50,19 @@ - + - Heute: {{ formatNumber($store.state.mqtt["openWB/optional/forecast/get/today_kwh"], 2, 2) || "0.00" }} + Aktuelle Prognose: Heute + {{ formatNumber($store.state.mqtt["openWB/optional/forecast/get/today_kwh"], 2, 2) || "0.00" }} kWh, morgen: {{ formatNumber($store.state.mqtt["openWB/optional/forecast/get/tomorrow_kwh"], 2, 2) || "0.00" }} kWh + - + +
+
+ + + +
+
{ const ts = Number(timestamp); - return Number.isFinite(ts) && ts >= nowSeconds && ts <= endSeconds; + if (!Number.isFinite(ts)) return false; + if (this.forecastDayFilter === "today") return ts >= todayStartSec && ts < tomorrowStartSec; + if (this.forecastDayFilter === "tomorrow") return ts >= tomorrowStartSec && ts < dayAfterStartSec; + return ts >= todayStartSec && ts < dayAfterStartSec; }), ); }, @@ -259,9 +298,20 @@ export default { responsive: true, maintainAspectRatio: false, parsing: false, + interaction: { + mode: "index", + intersect: false, + }, plugins: { - legend: { - display: true, + legend: { display: true }, + tooltip: { + callbacks: { + title: (items) => + items[0] + ? new Date(items[0].parsed.x).toLocaleString([], { dateStyle: "short", timeStyle: "short" }) + : "", + label: (item) => ` ${item.parsed.y.toFixed(2)} kW`, + }, }, }, scales: { @@ -287,6 +337,11 @@ export default { }, }; }, + lastUpdateTimeText() { + const timestamp = this.$store.state.mqtt["openWB/optional/forecast/get/last_update_time"]; + if (!timestamp) return "-"; + return new Date(Number(timestamp) * 1000).toLocaleString(); + }, nextQueryTimeText() { const timestamp = this.$store.state.mqtt["openWB/optional/forecast/get/next_query_time"]; if (!timestamp) { @@ -532,6 +587,6 @@ export default { From 4a14607e460d252884dc060dcb99d47722c8c613 Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sun, 9 Aug 2026 10:25:28 +0200 Subject: [PATCH 37/48] feat(forecast-ui): icon buttons for add/remove, azimuth hint as blue alert box --- .../forecast/forecastsolar/forecast.vue | 42 +++++++++++++------ .../forecast/openmeteo/forecast.vue | 42 +++++++++++++------ 2 files changed, 58 insertions(+), 26 deletions(-) diff --git a/src/components/forecast/forecastsolar/forecast.vue b/src/components/forecast/forecastsolar/forecast.vue index cfa3ddab8..8422a4c45 100644 --- a/src/components/forecast/forecastsolar/forecast.vue +++ b/src/components/forecast/forecastsolar/forecast.vue @@ -76,28 +76,44 @@ :model-value="row.azimuth" @update:model-value="updateStringField(index, 'azimuth', $event)" /> -

- Azimut: 0° = Sueden • -90° = Osten • 90° = Westen • 180° = Norden -

- + + Ausrichtung: 0° = Süden | -90° = Osten | 90° = Westen | 180° = Norden + +
+ +
- + From 371980a61fda6c025c2dd845a5ecc09cf2d7b8db Mon Sep 17 00:00:00 2001 From: seaspotter Date: Sun, 9 Aug 2026 11:14:53 +0200 Subject: [PATCH 41/48] feat(forecastsolar-ui): add optional API key field --- src/components/forecast/forecastsolar/forecast.vue | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/forecast/forecastsolar/forecast.vue b/src/components/forecast/forecastsolar/forecast.vue index e3b1f4002..3c4b1094f 100644 --- a/src/components/forecast/forecastsolar/forecast.vue +++ b/src/components/forecast/forecastsolar/forecast.vue @@ -1,7 +1,7 @@