PV Output Forecasting from forecast.solar

Moderator: leecollings

Post Reply
sidepipe
Posts: 29
Joined: Wednesday 18 February 2015 12:48
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

PV Output Forecasting from forecast.solar

Post by sidepipe »

Just in case anyone's interested, I wrote this little script to take data from forecast.solar and feed into 24 sensors which represent the forecasted output for each of the next 24 hours ( so you end up with 24 graphs, the first an hour ahead, then two, etc etc. ) It uses the first level paid for API ( it's not expensive and I think you should support things like this personally! ) but would be easy to modify to use the free version. As it is, you need to fill in your API key and solar details ( peak output and azimuth ) and create 24 energy usage counters named <basename> <hour> ( so if you leave the baseSensorName variable as is, you'll need "PV Forecast Hour 1", "PV Forecast Hour 2" .... "PV Forecast Hour 24". You also need to make sure your latitude and longitude are set in the Domoticz settings ( or else put them directly in the script. )

The script was originally inspired by one that waaren posted ( may you rest in peace sir :cry: )

Code: Select all

--[[ 
        get solar data from forecast.solar and store in a virtual sensors. The virtual sensors represent
        the forecast output for the next 24 hours in one hour increments - i.e. the first sensor is one
        hour ahead, the next two hours ahead, etc. The sensors should be named baseSensorName + " Hour " + n
        Currently designed for the personal account, so 30 minute data resolution. Adapted from the
        script by Waaren ( RIP ) here: https://www.domoticz.com/forum/viewtopic.php?t=27620
        
        Since we essentially want to create an advance forecast for up to 24 hours ahead, but the
        forecast.solar data just gives 30 minute intervals, we only use the closest result
        values to the appropriate number of hours ahead of each sensor.
        
--]] 

local webResponse = "solarData"

return  {   on =    {  
                       timer           = { 'every 30 minutes' },
                       httpResponses   = { webResponse },
                       devices         = { 'PV Forecast Manual Update' },
                    },

        logging =   {
                        level       = domoticz.LOG_INFO,
                        marker      = 'PVForecast',
                    },
       
    execute = function(dz, item)
        local apiKey = "Your API Key Here"                   -- API key for forecast.solar
        local latitude =  dz.settings.location.latitude     -- these are the lat/lon set in domoticz (dzVents >= 2.4.14 required)
        local longitude = dz.settings.location.longitude    -- or set to the value you want
        local planeDeclination = 45                         -- 0 (horizontal) … 90 (vertical)
        local planeAzimuth = -45                            --   -180 … 180 (-180 = north, -90 = east, 0 = south, 90 = west, 180 = north)
        local installedModulesPower = 5.985                 -- kWp
        local baseSensorName = "PV Forecast"
        
        local function getSolarData()
            local url = "https://api.forecast.solar" ..
                        "/" .. apiKey ..
                        "/estimate" ..
                        "/" .. latitude .. 
                        "/" .. longitude ..
                        "/" .. planeDeclination ..
                        "/" .. planeAzimuth ..
                        "/" .. installedModulesPower 
            dz.openURL  (   
                            {
                                url = url,
                                headers = { ['Accept'] = 'application/json' },
                                method = 'GET',
                                callback = webResponse
                            }        
                        )
        end
        
        local function processDataTable(table, array, aridx, name)
            local Time = require('Time')
            
            for key, value in pairs(table) do
                local cptime = Time(key).addMinutes(15)
                local diffmins = dz.time.compare(cptime).minutes

                dz.log('Process ' .. name .. ' for time ' .. key .. ' ( + 15 mins is ' .. cptime.getISO() .. ' ) which is ' .. diffmins ..
                    ' mins from now, value ' .. value, dz.LOG_DEBUG)
                -- See if this is a time within an acceptable window for an hour sensor.
                if (cptime.compare(Time()).compare < 0) and (diffmins % 60 < 30) then
                    local sensorno = math.floor((diffmins - diffmins % 60) / 60)
                    
                    if sensorno < 25 then
                        if array[sensorno] == nil then
                            array[sensorno] = { [0] = 0 }
                        end
                        
                        array[sensorno][aridx] = value
                        dz.log('Set values[' .. sensorno .. '][' .. aridx .. '] to ' .. value, dz.LOG_DEBUG)
                    end
                end
            end
        end
        
        
        if not item.isHTTPResponse then
            getSolarData()
        elseif item.ok then
            local resultTable = item.json.result
            local values = {}

            -- Put power and energy values into the values array indexed by 'hour from now'
            processDataTable(resultTable.watts, values, 0, 'power')
            processDataTable(resultTable.watt_hours, values, 1, 'energy')
            
            if values[0] == nil then
                values[0] = { [0] = 0, [1] = 0 }
            end

            prevE = values[0][1]

            -- Update sensors. Since energy values from forecast.solar are cumulative, we need to use differences for that.
            for l = 1,24 do
                local sensor = dz.devices(baseSensorName .. " Hour " .. l)
                
                if values[l] == nil then
                    -- This normal when there's no production forecast for this time slot.
                    dz.log('No information for hour ' .. l, dz.LOG_DEBUG)
                    sensor.updateElectricity(0, sensor.WhTotal)
                else
                    local e = values[l][1]
                    local p = values[l][0]
    
                    if e == nil then
                        e = 0
                    end

                    -- In theory this check could fail if entire production for a day was less than the first
                    -- reading of the next, but that's unlikely and not a disaster.
                    if e < prevE then
                        prevE = 0
                    end
                    
                    dz.log('Sensor ' .. l .. ': Power: ' .. p .. 'W, Energy: ' .. e .. 'Wh ( prev e was ' .. prevE ..
                        'Wh, sensor was ' .. sensor.WhTotal .. 'Wh', dz.LOG_DEBUG)
                    sensor.updateElectricity(p, sensor.WhTotal + (e - prevE)/2)
                    prevE = e
                    dz.log('prevE now set to ' .. prevE, dz.LOG_DEBUG)
                end
            end
        else
            dz.log("Problem with the JSON response",dz.LOG_ERROR) 
        end
   end
}
Edit: Fixed a couple of issues with the script....
1) Didn't realise that when you use time.compare().minutes it isn't signed, and since forecast.solar seems to also give you "forecast" values for previous times in the same day this was affecting the calculations.
2) Forgot to divide energy values by 2, since I'm calling the script every 30 minutes but calculating hourly values.
3) Missed the word "Hour" from the sensor names in the description text. I was obviously trying to rush this.... :?
Last edited by sidepipe on Tuesday 11 April 2023 10:07, edited 1 time in total.
willemd
Posts: 659
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: PV Output Forecasting from forecast.solar

Post by willemd »

Thanks for this.

I have been making my own calculation of theoretical PV production (using solar power data from a nearby weather station) to compare this to actual. Now I can compare this also to this data.

And having forecast data available adds another dimension. I can now make a script to tell my heating system to not heat up the room, because very soon the sun will do it, so no need to waste any gas on it.
Aziona
Posts: 12
Joined: Monday 04 March 2019 14:55
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: PV Output Forecasting from forecast.solar

Post by Aziona »

Thank you for the script. This is DZvents script?

By the way: noticed many postst from user Waaren. Did not know he passed away.
Aziona
Posts: 12
Joined: Monday 04 March 2019 14:55
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: PV Output Forecasting from forecast.solar

Post by Aziona »

I created the DZevents script, added the parameters like below, copied them from a working url (I have low cost subscription as well).
local apiKey = "abcdefB53sfabcdef"
local latitude = dz.settings.location.latitude
local longitude = dz.settings.location.longitude
local planeDeclination = 35
local planeAzimuth = -5
local installedModulesPower = 4.25
local baseSensorName = "PV Forecast"

I created 24 dummy sensors named PV Forecast Hour 1 until 24, type Custom sensor.

I am getting the following error in the log:
023-04-02 11:05:01.769 Error: dzVents: Error: (3.1.8) PVForecast: An error occurred when calling event handler ForecastSolar
2023-04-02 11:05:01.769 Error: dzVents: Error: (3.1.8) PVForecast: ...ticz/scripts/dzVents/generated_scripts/ForecastSolar.lua:120: attempt to concatenate a nil value (field 'WhTotal')

What could I have done wrong?
willemd
Posts: 659
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: PV Output Forecasting from forecast.solar

Post by willemd »

Best to first include a dumptable somewhere in your script so you can see whether you actually received correct data.
And then add log statement to check the assignment of values to variables like WhTotal.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest