Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Open-Meteo solar forecast #19111

Merged
merged 25 commits into from
Mar 4, 2025
Merged

Add Open-Meteo solar forecast #19111

merged 25 commits into from
Mar 4, 2025

Conversation

thecem
Copy link
Contributor

@thecem thecem commented Feb 23, 2025

No description provided.

thecem and others added 6 commits February 21, 2025 14:55
added octups DE api
added user&passwd auth
added token handling
intelligent octopus go tarif -> GoPrice / StandardPrice (Manual)
arif time 0-5 Go Rate 5-24 Standard Rate
static price for nowt
o do: use price information from api (if listet)
auth via email and password
pulls the first gross rate from the octopus api oeg-kraken.energy
Set the price for 24h to the gross rate, else to 23.25 cent/kWh
deleted:    templates/definition/tariff/octopus-germany.yaml
Erste Version des Open-Meteo Solar Forecast Tariffs
Brauche Unterstützung wie die Daten in den Tariff kommen.
!WIP!  -> NEED HELP!

Problems to get the data into the tariff.

Calculations and API Working!
!NEED HELP!

!!WIP!!

API -> Working
WhPeriod Calculation -> Working (Includs Damping!)
Tariff Solar: not working!
@andig andig added the tariffs Specific tariff support label Feb 23, 2025
@andig andig marked this pull request as draft February 23, 2025 18:08
@andig
Copy link
Member

andig commented Feb 23, 2025

Bitte PRs trennen!

@thecem
Copy link
Contributor Author

thecem commented Feb 23, 2025

Ich habe die für octopus geschlossen und wollte diesen hier separat öffnen VSC wollte das aber nicht wie ich.
Zugehörig ist:
#19005
und
#18940

Die sind aber nicht zu betrachten der Octopus läuft braucht aber noch anpassungen (WIP).

Hier mit dem Open-Meteo komme ich nicht weiter die Daten an die Tariff api so zu parsen das es geht. Hier brauche ich hilfe. Ich hatte zwischendrin probleme mit dem Zeitformat {parseISO8601ToRFC3339} (could not parse "" for ":") aber das ist jetzt gelöst, dafür taucht dann in der UI nicht mehr der die Vorhersage auf. Ich denke es hat was mit dem format der daten zur tariff api zu tun.

@andig
Copy link
Member

andig commented Feb 23, 2025

Unabhängig davon bitte ein PR pro Thema!

@thecem
Copy link
Contributor Author

thecem commented Feb 23, 2025 via email

@andig
Copy link
Member

andig commented Feb 23, 2025

Egal wie- getrennte prs bitte. Ein Branch und damit pr je Thema.

@thecem
Copy link
Contributor Author

thecem commented Feb 23, 2025

Ich hoffe das passt jetzt so, ich habe die jetzt rausgelöscht.
nur noch die hier:
image

@thecem
Copy link
Contributor Author

thecem commented Feb 24, 2025

Ich werde das noch stark vereinfachen. Der Vorteil von Open-Meteo ist die hohe Anzahl von Calls und die hohe Genauigkeit.
Deshalb bitte ich hier um Unterstützung wie ich die Daten in die Tarif.api bekomme.

@zachelnet
Copy link
Contributor

zachelnet commented Feb 25, 2025

Ich werde das noch stark vereinfachen. Der Vorteil von Open-Meteo ist die hohe Anzahl von Calls und die hohe Genauigkeit. Deshalb bitte ich hier um Unterstützung wie ich die Daten in die Tarif.api bekomme.

Sieht interessant aus.
Eventuell würde ich den Teil mit der Webservice-Abfrage in das Template integrieren und dann die Werte an das Berechnungsmodell übergeben, damit die Werte entsprechend der Prognose ausgegeben werden. Dann könnte man diese Werte über das Template an Tarif/Solcast übergeben.

Dann bräuchte man nur noch Code für die Berechnung und entsprechend das Template. Dies würde die Wartbarkeit sehr vereinfachen und eventuell könnten noch weitere APIs entsprechend angebunden werden oder alternativ das Berechnungsmodell ausgetauscht werden.

@thecem
Copy link
Contributor Author

thecem commented Feb 26, 2025

Am Wochenende kommt eine neue einfach Lösung.

thecem added 2 commits March 1, 2025 17:55
Only a template file is left, the rest is removed.
The Open-Meteo tariff is now a simple example for a tariff definition.

  solar:
    type: template
    template: open-meteo
    lat: 50.1592
    lon: 8.9830
    az: 0
    dec: 37
    dckwp: 125
    ackwp: 9
    eff: 1
    dm: 0
    de: 0
mandatory:
    lat: 50.1592
    lon: 8.9830
    az: 0
    dec: 37
    dckwp: 125
optional:
    ackwp: 9
    eff: 1
    dm: 0
    de: 0
@thecem
Copy link
Contributor Author

thecem commented Mar 1, 2025

Massiv simplification of the Open-Meteo tariff.

Only a template file is left, the rest is removed.
The Open-Meteo tariff is now a simple example for a tariff definition.

solar:
  type: template
  template: open-meteo
  lat: 50.1592
  lon: 8.9830
  az: 0
  dec: 37
  dckwp: 125
  ackwp: 9
  eff: 1
  dm: 0
  de: 0
mandatory:
  lat: 50.1592
  lon: 8.9830
  az: 0
  dec: 37
  dckwp: 125
optional:
  ackwp: 9
  eff: 1
  dm: 0
  de: 0

@thecem
Copy link
Contributor Author

thecem commented Mar 1, 2025

if you can´t wait for release and would like to test now:

Edit this for your location and alignment: latitude=50.1592&longitude=8.9830&azimuth=180&tilt=37

tariffs:
 solar:
   type: custom
   forecast:
     source: http
     uri: https://api.open-meteo.com/v1/forecast?latitude=50.1592&longitude=8.9830&azimuth=180&tilt=37&hourly=temperature_2m,global_tilted_irradiance,global_tilted_irradiance_instant&daily=sunrise,sunset&forecast_days=3&timezone=auto&timeformat=unixtime
     jq: |
       
       def eff: 0.83; # efficiency 1 = 100%
       
       def dckwp: 135.2; # DC-kWp

       def ackwp: 9.0; # AC-kWp

       def dm: 0; # 1 = 100% damping morning (0 = no damping)

       def de: 0; # damping evening

       def clamp(min; x; max):
         if x < min then min elif x > max then max else x end;
       def midday(sunrise; sunset):
         sunrise + ((sunset - sunrise) / 2);
       def calculate_damping(time; sunrise; sunset; m; e):
         if time < sunrise then m
         elif time < midday(sunrise; sunset) then
           m * (1 - ((time - sunrise) / (midday(sunrise; sunset) - sunrise)))
         elif time < sunset then
           e * ((time - midday(sunrise; sunset)) / (sunset - midday(sunrise; sunset)))
         else
           e
         end;
       .hourly as $h
       | .daily as $d
       | [ range(0; ($h.time | length))
           | . as $i
           | $h.time[$i] as $time
           | ($i / 24 | floor) as $day
           | {
               start: ($time | todateiso8601),
               end: (($time + 3600) | todateiso8601),
               price: (
                 (dckwp * 1000)
                 * ($h.global_tilted_irradiance[$i] / 1000)
                 * (1 + (-0.00045 *
                       (
                         ( ($h.temperature_2m[$i]
                             + ($h.temperature_2m[$i-1] // $h.temperature_2m[$i])
                           ) / 2)
                         + ($h.global_tilted_irradiance[$i] / 800.0) * 0.0342 - 25.0
                       )
                     ))
                 * (1 - calculate_damping($time;
                       $d.sunrise[$day];
                       $d.sunset[$day];
                       dm; de))
                 * eff
                 | clamp(0; .; (ackwp * 1000))
               )
             }
         ]
       | tostring

@andig andig changed the title Open-Meteo Solar Forecast Add Open-Meteo solar forecast Mar 1, 2025
@CiNcH83
Copy link

CiNcH83 commented Mar 1, 2025

Are you sure that azimuth is [-180,180]? On HA it is [0,360] for Open-Meteo...

@thecem
Copy link
Contributor Author

thecem commented Mar 2, 2025

The term azimuth refers to a horizontal angle measured relative to the cardinal directions. There are two main types of azimuth measurements:
1. South Azimuth: The measurement starts in the south (0°) and proceeds through west (90°), north (180°), to east (270°).
2. North Azimuth: This system begins in the north (0°) and moves clockwise through east (90°), south (180°), to west (270°). This method is commonly used in navigation and geodesy.
The choice of convention depends on the application, such as astronomy or navigation.

The template uses the API based south azimuth:

Total radiation received on a tilted pane as average of the preceding hour. The calculation is assuming a fixed albedo of 20% and in isotropic sky. Please specify tilt and azimuth parameter. Tilt ranges from 0° to 90° and is typically around 45°. Azimuth should be close to 0° (0° south, -90° east, 90° west). If azimuth is set to "nan", the calculation assumes a horizontal tracker. If tilt is set to "nan", it is assumed that the panel has a vertical tracker. If both are set to "nan", a bi-axial tracker is assumed.

https://open-meteo.com/en/docs

@GrimmiMeloni
Copy link
Collaborator

Fixed the linter here locally (a new line is missing at the end of the template), but a PR leads to a huge changeset. I am not that good at working with git "triangles". :(

@GrimmiMeloni
Copy link
Collaborator

You may want to run make lint-ui locally, to recreate the checks that the GHA does.

@tantive
Copy link

tantive commented Mar 2, 2025

@thecem Is there a reason why 'global_tilted_irradiance_instant' is fetched? The calculation does not seem to use it.

Tested manually as described in #19111 (comment) - works like a charm ;)

@thecem
Copy link
Contributor Author

thecem commented Mar 2, 2025

You are rigt this is at the moment not nessesary.

@zachelnet
Copy link
Contributor

zachelnet commented Mar 2, 2025

Hi @thecem thanks for the code, it looks good, it works without the optional value.

During my test with the following configuration with east and west orientation, regarding the option dm and de, the following error messages appear.

config:

- type: template
  # East 
  template: open-meteo
  lat: 48.000000
  lon: 10.000000
  dec: 45
  az: -74
  dckwp: 4.3
  de: 0.5
- type: template
  # West
  template: open-meteo
  lat: 48.000000
  lon: 10.000000
  dec: 45
  az: 74
  dckwp: 4.3
  dm: 0.5

log messages:

[main ] ERROR 2025/03/02 18:10:11 creating tariff solar-template-1 failed: cannot create tariff type 'template': cannot create tariff type 'custom': forecast: invalid jq query 'def eff: 1; # efficiency 1 = 100% def dckwp: 4.3 * 1000 ; # DC-kWp def ackwp: 1000 * 1000 ; # AC-kWp def dm: '0.5'; # 1 = 100% damping morning (0 = no damping) def de: 0; # damping evening def clamp(min; x; max): if x < min then min elif x > max then max else x end; def midday(sunrise; sunset): sunrise + ((sunset - sunrise) / 2); def calculate_damping(time; sunrise; sunset; m; e): if time < sunrise then m elif time < midday(sunrise; sunset) then m * (1 - ((time - sunrise) / (midday(sunrise; sunset) - sunrise))) elif time < sunset then e * ((time - midday(sunrise; sunset)) / (sunset - midday(sunrise; sunset))) else e end; .hourly as $h | .daily as $d | [ range(0; ($h.time | length)) | . as $i | $h.time[$i] as $time | ($i / 24 | floor) as $day | { start: ($time | todateiso8601), end: (($time + 3600) | todateiso8601), price: ( dckwp * ($h.global_tilted_irradiance[$i] / 1000) * (1 + (-0.00045 * ( ( ($h.temperature_2m[$i] + ($h.temperature_2m[$i-1] // $h.temperature_2m[$i]) ) / 2) + ($h.global_tilted_irradiance[$i] / 800.0) * 0.0342 - 25.0 ) )) * (1 - calculate_damping($time; $d.sunrise[$day]; $d.sunset[$day]; dm; de)) * eff | clamp(0; .; ackwp) ) } ] | tostring ': unexpected token "'"
[main ] ERROR 2025/03/02 18:10:11 creating tariff solar-template-0 failed: cannot create tariff type 'template': cannot create tariff type 'custom': forecast: invalid jq query 'def eff: 1; # efficiency 1 = 100% def dckwp: 4.3 * 1000 ; # DC-kWp def ackwp: 1000 * 1000 ; # AC-kWp def dm: 0; # 1 = 100% damping morning (0 = no damping) def de: '0.5'; # damping evening def clamp(min; x; max): if x < min then min elif x > max then max else x end; def midday(sunrise; sunset): sunrise + ((sunset - sunrise) / 2); def calculate_damping(time; sunrise; sunset; m; e): if time < sunrise then m elif time < midday(sunrise; sunset) then m * (1 - ((time - sunrise) / (midday(sunrise; sunset) - sunrise))) elif time < sunset then e * ((time - midday(sunrise; sunset)) / (sunset - midday(sunrise; sunset))) else e end; .hourly as $h | .daily as $d | [ range(0; ($h.time | length)) | . as $i | $h.time[$i] as $time | ($i / 24 | floor) as $day | { start: ($time | todateiso8601), end: (($time + 3600) | todateiso8601), price: ( dckwp * ($h.global_tilted_irradiance[$i] / 1000) * (1 + (-0.00045 * ( ( ($h.temperature_2m[$i] + ($h.temperature_2m[$i-1] // $h.temperature_2m[$i]) ) / 2) + ($h.global_tilted_irradiance[$i] / 800.0) * 0.0342 - 25.0 ) )) * (1 - calculate_damping($time; $d.sunrise[$day]; $d.sunset[$day]; dm; de)) * eff | clamp(0; .; ackwp) ) } ] | tostring ': unexpected token "'"

@thecem
Copy link
Contributor Author

thecem commented Mar 2, 2025

Many thanks for heads-up! Could you do me a favor and try it without the zero in front of the dot:

- type: template
  # East 
  template: open-meteo
  lat: 48.768210
  lon: 10.799644
  dec: 45
  az: -74
  dckwp: 4.3
  de: .5

and with a 1 in front of?

- type: template
  # East 
  template: open-meteo
  lat: 48.768210
  lon: 10.799644
  dec: 45
  az: -74
  dckwp: 4.3
  de: 1.5

What is the result?

@zachelnet
Copy link
Contributor

zachelnet commented Mar 2, 2025

Hi @thecem

- type: template
  # East 
  template: open-meteo
  lat: 48.000000
  lon: 10.000000
  dec: 45
  az: -74
  dckwp: 4.3
  de: .5

Doesn't work with `.5':

[main ] ERROR 2025/03/02 19:47:37 creating tariff solar-template-0 failed: cannot create tariff type 'template': cannot create tariff type 'custom': forecast: invalid jq query 'def eff: 1; # efficiency 1 = 100% def dckwp: 4.3 * 1000 ; # DC-kWp def ackwp: 8 * 1000 ; # AC-kWp def dm: 0; # 1 = 100% damping morning (0 = no damping) def de: '0.5'; # damping evening def clamp(min; x; max): if x < min then min elif x > max then max else x end; def midday(sunrise; sunset): sunrise + ((sunset - sunrise) / 2); def calculate_damping(time; sunrise; sunset; m; e): if time < sunrise then m elif time < midday(sunrise; sunset) then m * (1 - ((time - sunrise) / (midday(sunrise; sunset) - sunrise))) elif time < sunset then e * ((time - midday(sunrise; sunset)) / (sunset - midday(sunrise; sunset))) else e end; .hourly as $h | .daily as $d | [ range(0; ($h.time | length)) | . as $i | $h.time[$i] as $time | ($i / 24 | floor) as $day | { start: ($time | todateiso8601), end: (($time + 3600) | todateiso8601), price: ( dckwp * ($h.global_tilted_irradiance[$i] / 1000) * (1 + (-0.00045 * ( ( ($h.temperature_2m[$i] + ($h.temperature_2m[$i-1] // $h.temperature_2m[$i]) ) / 2) + ($h.global_tilted_irradiance[$i] / 800.0) * 0.0342 - 25.0 ) )) * (1 - calculate_damping($time; $d.sunrise[$day]; $d.sunset[$day]; dm; de)) * eff | clamp(0; .; ackwp) ) } ] | tostring ': unexpected token "'"

and with a 1 in front of?

- type: template
  # East 
  template: open-meteo
  lat: 48.000000
  lon: 10.000000
  dec: 45
  az: -74
  dckwp: 4.3
  de: 1.5

What is the result?

The result with '1.5' it's work:

[solar-template-0] TRACE 2025/03/02 19:49:21 GET https://api.open-meteo.com/v1/forecast?latitude=48.76821&longitude=10.799644&azimuth=-74&tilt=45&hourly=temperature_2m,global_tilted_irradiance&daily=sunrise,sunset&forecast_days=3&timezone=auto&timeformat=unixtime

The received values are in some cases stings instead of numeric values.
This is a temporary fix until the issue is resolved.
When a 0.5 value is received, the value is in some cases string.
I think this belongs to a problem in the class_enummer.go file.
The values are not converted to float64 in the class_enummer.go file.
@thecem
Copy link
Contributor Author

thecem commented Mar 2, 2025

There is no problem with the script, but a problem with the type of input values in the template mechanism.

I fixed it with a workaround. Now you have to set 0-100 values.

@zachelnet
Copy link
Contributor

zachelnet commented Mar 2, 2025

@thecem thanks looks good

My current config looks like:

solar:
- type: template
  # Ost 
  template: open-meteo
  lat: 48.000000
  lon: 10.000000
  dec: 45
  az: -74
  dckwp: 4.3
  de: 40
  eff: 75
- type: template
  # West
  template: open-meteo
  lat: 48.000000
  lon: 10.000000
  dec: 45
  az: 74
  dckwp: 4.3
  dm: 40
  eff: 75

@tantive
Copy link

tantive commented Mar 2, 2025

@thecem Does it make sense to allow configuration of other constants used in the calculcation e.g. temperature coefficient of the used modules?

@CiNcH83
Copy link

CiNcH83 commented Mar 2, 2025

@zachelnet I assume you have one 8 kW inverter? You specified an 8 kW inverter for each orientation. Guess it won't harm too much in this case. But probably it is better to omit AC power in such a case as it is optional anyway?

thecem added 4 commits March 2, 2025 20:25
Integrating DE API OctopusEnergy tariff for Germany.
SimpleProduct and TimeOfUseProduct are supported.
In case of TimeOfUseProduct, the tariff shows the rates for the TimeOfUseProduct.
In case of SimpleProduct, the tariff shows the rates for the SimpleProduct for 72 hours.
If both SimpleProduct and TimeOfUseProduct are not available, the tariff shows the rates of 0.2827.
The intelligent dispatching of the tariff will be added in the next PR.
The tariff is tested with the OctopusEnergy DE API.
@thecem thecem marked this pull request as ready for review March 4, 2025 16:38
@thecem thecem requested a review from andig March 4, 2025 17:27
deleted repeating pv module
added paper link for the rossmodel
added help text for the rossmodel
@thecem thecem requested a review from andig March 4, 2025 18:29
@andig andig merged commit b638445 into evcc-io:master Mar 4, 2025
6 checks passed
@andig
Copy link
Member

andig commented Mar 4, 2025

Great PR, thank you. Just be aware that you'll need to maintain this until the end- my jq-foo is not up for this ;)

@andig
Copy link
Member

andig commented Mar 6, 2025

@thecem please see #19463 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tariffs Specific tariff support
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants