From 6ba804b106ed2a9afbaedad7a18fe616514ce1b1 Mon Sep 17 00:00:00 2001 From: Michael Renner Date: Fri, 27 Jun 2025 08:52:02 +0200 Subject: [PATCH] Add demo solar forecast template (#21971) Co-authored-by: Michael Geers --- cmd/demo.yaml | 6 + .../tariff/demo-solar-forecast.yaml | 108 ++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 templates/definition/tariff/demo-solar-forecast.yaml diff --git a/cmd/demo.yaml b/cmd/demo.yaml index 3692b1fca..9141f761a 100644 --- a/cmd/demo.yaml +++ b/cmd/demo.yaml @@ -262,6 +262,12 @@ tariffs: feedin: type: fixed price: 0.08 # EUR/kWh + solar: + type: template + template: demo-solar-forecast + kwp: 15.5 + sunrise: 7 + sunset: 17 #co2: # type: grünstromindex # zip: 12349 diff --git a/templates/definition/tariff/demo-solar-forecast.yaml b/templates/definition/tariff/demo-solar-forecast.yaml new file mode 100644 index 000000000..2d1c2bf4f --- /dev/null +++ b/templates/definition/tariff/demo-solar-forecast.yaml @@ -0,0 +1,108 @@ +template: demo-solar-forecast +products: + - description: + de: Demo PV Vorhersage + en: Demo PV Forecast +requirements: + description: + de: Zu Demonstrationszwecken. Liefert optimale Solarproduktionskurve mit Spitze zur Mittagszeit. + en: For demonstration purposes. Provides optimal solar production curve peaking at noon. + evcc: ["skiptest"] +group: solar +params: + - name: kwp + type: float + unit: kWp + default: 4.5 + description: + en: Maximum generator power + de: Maximale Generatorleistung + - name: sunrise + description: + de: Sonnenaufgang + en: Sunrise hour + type: int + unit: h + default: 6 + help: + de: Stunde des Sonnenaufgangs (0-23) + en: Hour of sunrise (0-23) + - name: sunset + description: + de: Sonnenuntergang + en: Sunset hour + type: int + unit: h + default: 18 + help: + de: Stunde des Sonnenuntergangs (0-23) + en: Hour of sunset (0-23) + - name: interval + default: 1h + advanced: true + +render: | + type: custom + tariff: solar + forecast: + source: js + script: | + // Generate realistic solar forecast with bell curve for next 72 hours + var now = new Date(); + var start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0); + var rates = []; + var peakPower = parseFloat({{ .kwp }}); + var sunrise = parseInt({{ .sunrise }}); + var sunset = parseInt({{ .sunset }}); + + // Calculate day parameters with inclusive hours + // Using modulo to handle midnight-spanning days elegantly + var dayLength = (sunset - sunrise + 1 + 24) % 24 || 24; + + var is24HourDaylight = dayLength === 24; + var noon = (sunrise + dayLength / 2) % 24; + + // Helper function to calculate position within daylight period + function getPositionInPeriod(hour) { + if (is24HourDaylight) { + // 24-hour daylight: distance from noon with wrap-around + var diff = Math.abs(hour - noon); + return Math.min(diff, 24 - diff); + } else { + // Regular day: hours from sunrise + return hour >= sunrise ? hour - sunrise : 24 - sunrise + hour; + } + } + + // Generate hourly rates for 3 days + for (var day = 0; day < 3; day++) { + for (var hour = 0; hour < 24; hour++) { + var time = new Date(start.getTime() + (day * 24 + hour) * 3600000); + var solarValue = 0; + + if (dayLength > 0) { + // Check if hour is within daylight period + var isDaylight = is24HourDaylight || (sunrise <= sunset + ? hour >= sunrise && hour <= sunset + : hour >= sunrise || hour <= sunset); + + if (isDaylight) { + var position = getPositionInPeriod(hour); + var normalized = is24HourDaylight + ? position / 12 + : (position - dayLength / 2) / (dayLength / 2); + var curve = 1 - normalized * normalized * (is24HourDaylight ? 0.89 : 1); + solarValue = peakPower * 1e3 * curve; + } + } + + rates.push({ + start: time.toISOString(), + end: new Date(time.getTime() + 3600000).toISOString(), + value: Math.round(Math.max(0, solarValue)) + }); + } + } + + JSON.stringify(rates); + interval: {{ .interval }}