# Solar forecast

URL: https://pro-weather.com/docs/features/solar-forecast

> An hour-by-hour power forecast for your own solar panels, built from weather models scored against your station''s solar sensor, with an API for evcc, EMHASS and Home Assistant.

If you have solar panels, a battery or an electric car, the useful question
about tomorrow is how many kilowatt-hours your roof will make and when. The
solar forecast answers it for your own panels, hour by hour for the next two
days, and hands the numbers to the software that schedules your charging.

It is part of the Pro plan. Set it up under **Setup > Solar forecast** in your
site's dashboard.

## How it works

1. Every hour, Pro Weather fetches the sunshine forecast (solar radiation on a
   flat surface) from several weather models for your station's location.
2. It checks each model's earlier forecasts against what your station's solar
   sensor actually measured, and gives the models that got your sky right
   more say. That is the part a generic solar forecast cannot do: it has no
   sensor on your roof.
3. It turns the result into power for each of your panel arrays: the sun's
   position, the angle your panels face, the air temperature (hot panels make
   less), your system losses and your inverter's limit.

The forecast says **corrected** once your solar sensor has scored the models
for a couple of days. Until then, and for a station without a solar sensor,
every model counts the same. It still works; it just has not learned your sky
yet.

What your solar sensor cannot know: shade from trees or chimneys, and panels
that need a wash. It sees the sky, not your roof. Send what your panels
actually make from Home Assistant and the forecast learns that too (see
[Teach it your roof](#teach-it-your-roof)).

## Setting up your panels

Add one row per roof face, up to four:

- **kWp**: the peak power of that array, from your installer's paperwork or
  the panel count times the panel rating.
- **Tilt**: 0 for flat panels, 90 for panels on a wall. A typical pitched roof
  is 30 to 45.
- **Azimuth**: where the panels face. 0 is south, -90 east, 90 west, 180
  north. South-east is -45.
- **Losses**: wiring, dirt and the inverter. 14 % suits most systems; lower it
  if the forecast keeps coming in under what your inverter reports.
- **Inverter kW**: the most the inverter can put out. Leave it empty if you do
  not know.

An east-west roof is two rows, one facing -90 and one facing 90.

## Connect evcc

[evcc](https://evcc.io) uses a solar forecast to decide when to charge your
car. Create a token under **Setup > Solar forecast**, then add this to
`evcc.yaml` with your token in place of `<your token>`:

```yaml
tariffs:
  solar:
    - type: custom
      tariff: solar
      forecast:
        source: http
        uri: https://pro-weather.com/api/solar
        headers:
          - Authorization: Bearer <your token>
        jq: .slots | tostring
      interval: 1h
```

## Connect EMHASS or a script

[EMHASS](https://emhass.readthedocs.io) and most battery schedulers accept a
list of watts per time step. With a 60-minute optimization time step, pass the
`value` of each slot:

```bash
curl -s -H "Authorization: Bearer <your token>" https://pro-weather.com/api/solar \
  | jq '{pv_power_forecast: [.slots[].value]}'
```

and post that to EMHASS's `dayahead-optim` action. Node-RED, Home Assistant
automations and your own scripts can read the same URL.

## Home Assistant

The Pro Weather Solar Forecast integration puts the forecast in Home
Assistant's Energy dashboard and gives you sensors for automations. Install
it through [HACS](https://hacs.xyz) as a custom repository:

1. In HACS, open the three-dot menu, choose **Custom repositories**, add
   `https://github.com/LNNRT-STN/ha-pro-weather` as an **Integration**, then
   download **Pro Weather Solar Forecast** and restart Home Assistant.
2. Under **Settings > Devices & services > Add integration**, pick **Pro
   Weather Solar Forecast** and paste your token.
3. Under **Settings > Dashboards > Energy**, edit your solar production and
   tick the Pro Weather entry as its **Solar production forecast**.

The integration adds power now, energy this hour and next, energy today,
remaining today and tomorrow, and today's peak. "Today" follows your Home
Assistant time zone. A **Corrected by station** sensor shows whether your
solar sensor has scored the models yet. If you regenerate the token, Home
Assistant asks for the new one.

### Teach it your roof

The forecast knows the sky over your station, not the tree that shades your
panels after four or the dust on them. Your inverter does. In the
integration's options (**Settings > Devices & services > Pro Weather Solar
Forecast > Configure**), pick **Solar production sensor**: the energy sensor
in kWh that counts what your panels make, the same one the Energy dashboard
uses for solar production. Once an hour the integration sends each finished
hour of the last day to Pro Weather.

From those hours Pro Weather learns one number for your site: how much of the
forecast your panels actually deliver. It uses bright hours only, and, where
your station measures sunshine, only hours whose sky the forecast got right,
so a cloudy surprise does not count against your roof. Hours with a power
cut, snow on the panels, a full battery that switched the panels off, or an
inverter or export limit holding the output flat are left out. After 24 good
hours the forecast starts using it, and it keeps learning over the last 30
days, so it follows the seasons and a panel clean.

**Setup > Solar forecast** shows the number once it is learned, for example
"your panels make 92 % of what the forecast expected". It never goes below
50 % or above 130 %: a gap that big means the kWp, tilt or azimuth is wrong,
and the forecast should show that rather than hide it. Changing your panels
starts the calculation over from the hours already sent.

A battery that is often full while the house uses only part of the sun can
still make the number read low, because your inverter then produces less than
the roof could. If that is your setup, keep an eye on it in late spring.

For [Predbat](https://springfall2008.github.io/batpred/), the energy today and
tomorrow sensors carry a half-hourly `detailedForecast` attribute in the same
shape as the Solcast integration's. Point `pv_forecast_today` and
`pv_forecast_tomorrow` in Predbat's `apps.yaml` at those two sensors. Its
`pv_estimate10` and `pv_estimate90` are the cautious and bright case described
under [The API](#the-api), so Predbat's pessimistic weighting has a real range
to work with (from integration version 0.2.0).

### Without the integration

A REST sensor gives you the rest of today's forecast for automations, with
nothing to install. In `secrets.yaml`, store the whole header value:
`proweather_solar: "Bearer <your token>"`, then:

```yaml
rest:
  - resource: https://pro-weather.com/api/solar
    headers:
      Authorization: !secret proweather_solar
    scan_interval: 1800
    sensor:
      - name: Solar forecast rest of today
        unit_of_measurement: kWh
        value_template: "{{ ((value_json.daily.values() | list)[0] / 1000) | round(1) }}"
```

A REST sensor cannot feed the Energy dashboard's forecast line; only an
integration can.

## The API

`GET https://pro-weather.com/api/solar` with the header
`Authorization: Bearer <your token>` returns the next 48 hours:

```json
{
  "generatedAt": "2026-09-24T09:12:03.120Z",
  "issuedAt": "2026-09-24T09:00:00.000Z",
  "corrected": true,
  "station": { "name": "Ardooie", "timezone": "Europe/Brussels" },
  "arrays": [{ "name": "Roof south", "kwp": 5.2 }],
  "scale": { "active": true, "value": 0.92, "samples": 212 },
  "slots": [
    { "start": "2026-09-24T09:00:00Z", "end": "2026-09-24T10:00:00Z", "value": 2140, "wh": 2140, "ghi": 512, "p10": 1310, "p90": 2780 }
  ],
  "wh_hours": { "2026-09-24T09:00:00Z": 2140 },
  "daily": { "2026-09-24": 14800, "2026-09-25": 21300 },
  "daily_p10": { "2026-09-24": 9600, "2026-09-25": 13900 },
  "daily_p90": { "2026-09-24": 18900, "2026-09-25": 26100 }
}
```

- `slots`: one per hour, `value` the average power in watts, `wh` the energy
  in watt-hours, `ghi` the sunshine forecast behind it in W/m². Times are UTC.
  `p10` and `p90` are the cautious and bright case in watts: one hour in ten
  is expected to come in below `p10`, and one in ten above `p90`. The band
  is sized from how far your own station's solar sensor has landed from the
  forecast; a station without a sensor borrows the width measured at other
  stations. At night, and on a forecast from before the band existed, both
  equal `value`.
- `wh_hours`: the same energy keyed by the start of each hour.
- `daily_p10`, `daily_p90`: each day's hourly `p10` and `p90` added up. A
  whole day at every hour's cautious case is rarer than one day in ten, so
  treat `daily_p10` as a floor for a battery reserve, not as a typical bad
  day.
- `daily`: watt-hours per day in your station's timezone, starting with the
  rest of today.
- `scale`: what your measured production has taught the forecast. `value` is
  already applied to every number above; it is 1 while `active` is false, and
  `samples` is how many hours it rests on.
- A new forecast is ready once an hour; polling every 15 to 60 minutes is
  plenty. More than 60 requests an hour from one address get a `429`.

Errors: `401` for a missing or unknown token, `403` when the site's plan does
not include the solar forecast, `409` when no panels are set up or the station
has no location. Creating a new token stops the old one immediately.

### Sending production from your own script

The Home Assistant integration does this for you. Anything else that knows
your hourly production can post it with the same token:

```bash
curl -s -X POST https://pro-weather.com/api/solar/production \
  -H "Authorization: Bearer <your token>" -H "Content-Type: application/json" \
  -d '{"hours": [{"start": "2026-09-24T09:00:00Z", "wh": 2140}]}'
```

- `start` is the start of a finished hour in UTC, on the hour. `wh` is what
  all your panels together made in that hour, in watt-hours.
- Up to 168 hours per request, none older than 7 days. Sending an hour again
  replaces it, so resending the last day every hour is fine and fills any
  gap.
- The whole request is refused with `400` and the bad hour named if any hour
  is wrong, for example not finished yet or more than your panels could make.
  `413` above 32 KB, `415` without the JSON content type, and more than 30
  requests an hour from one address get a `429`.
- The answer says how many hours were accepted and the `scale` as it stands.
