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

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
}
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....
