It is a rolling forecast meaning it will only look at the future.
Because the information is a prediction about the future you could just take ( min + max ) / 2 for this just after midnight.
After this update I will no longer add new functionality to the script because there is an excellent native hardware module available. I will only fix bugs.
Code: Select all
--[[
This script collects rain en temperature forecast data from openweathermap.org
You need a free API key to access this data. Read howto get this free API key
at https://openweathermap.org/guide#how
Next you need to create a uservariable (type string) and enter the openweathermap API key as value
Then create the devices you want
Please note that dzVents requires access to domoticz at a sufficient level to retrieve the location settings
including the latitude and longitude. It can only get these if the Local Networks (not using username/password)
include 127.0.0.1 (and ::1 for systems using IPv6)
Explained in the wiki at: https://www.domoticz.com/wiki/DzVents:_next_generation_LUA_scripting#Using_dzVents_with_Domoticz
original script posted: https://www.domoticz.com/forum/viewtopic.php?f=59&t=33424&start=20
history
20200720 Start coding - shared on forum
20200722 Added average and minimum temperature devices
20200806 Added optional virtual devices for max. temperature today, max temperature tomorrow, mm rain today, mm rain tomorrow.
20201109 Defined devices in separate table so the will only be visited when required
20200116 Add minimum temperature (rest of) today and tomorrow
]]--
local scriptVar = 'openWeatherMap'
return
{
on = {
timer =
{
'at *:17'
},
devices =
{
scriptVar .. 'Trigger', -- only used for testing
},
httpResponses =
{
scriptVar
}
},
logging =
{
level = domoticz.LOG_DEBUG, -- switch to domoticz.ERROR when script works as designed
marker = scriptVar,
},
execute = function(dz, item)
-- ******** enter name of API var and devices below this line
local openWeatherAPIKey = dz.variables('openWeatherAPIKey').value -- Type string var required
if item.isHTTPResponse then
-- ******** enter names of used devices between the quotes below this line
dv = {}
dv.currentRain = dz.devices('Current rain') -- optional virtual Custom Sensor
dv.rain48Hours = dz.devices('Rain 48 hours') -- optional virtual Custom Sensor
dv.rain8Days = dz.devices('Rain 8 days') -- optional virtual Custom Sensor
dv.rainToday = dz.devices('Rain today') -- optional virtual Custom Sensor
dv.rainTomorrow = dz.devices('Rain tomorrow') -- optional virtual Custom Sensor
dv.weatherText = dz.devices('WeatherForecast') -- optional text Sensor
dv.currentTemperature = dz.devices('Current Temperature') -- Optional virtual Temperature sensor
dv.maxTemperature48Hours = dz.devices('Max Temperature next 48 hours') -- Optional virtual Temperature sensor
dv.maxTemperature8Days = dz.devices('Max Temperature next 8 days') -- Optional virtual Temperature sensor
dv.minTemperature48Hours = dz.devices('Min Temperature next 48 hours') -- Optional virtual Temperature sensor
dv.minTemperature8Days = dz.devices('Min Temperature next 8 days') -- Optional virtual Temperature sensor
dv.maxTemperatureToday = dz.devices('Max Temperature today') -- Optional virtual Temperature sensor
dv.maxTemperatureTomorrow = dz.devices('Max Temperature tomorrow') -- Optional virtual Temperature sensor
dv.avgTemperature48Hours = dz.devices('Average Temperature next 48 hours') -- Optional virtual Temperature sensor
dv.avgTemperature8Days = dz.devices('Average Temperature next 8 days') -- Optional virtual Temperature sensor
dv.minTemperatureToday = dz.devices('Min Temperature today') -- Optional virtual Temperature sensor
dv.minTemperatureTomorrow = dz.devices('Min Temperature tomorrow') -- Optional virtual Temperature sensor
end
-- ******** No changes required below this line
local openWeatherURL = 'https://api.openweathermap.org/data/2.5/onecall?&units=metric'
local DAY_SECONDS = 86400
local endOfToday = os.time{year = dz.time.year, month = dz.time.month, day = dz.time.day, hour = 0} + DAY_SECONDS
local endOfTomorrow = endOfToday + DAY_SECONDS
local function getCurrentRain(t)
if t == nil then return 0 end
local totalmm = 0
for key, mm in pairs(t) do
totalmm = totalmm + mm
end
return totalmm
end
local function getDayNightSeconds(t)
t.daySeconds = t.sunset - t.sunrise
t.nightSeconds= DAY_SECONDS - t.daySeconds
end
local function get8DaysTemperatures(t)
local maxTemperature = -100
local minTemperature = 100
local avgTemperature, avgHelper = 0, 0
for _, record in ipairs(t) do
getDayNightSeconds(record)
if record.temp.max > maxTemperature then maxTemperature = record.temp.max end
if record.temp.min < minTemperature then minTemperature = record.temp.min end
avgTemperature = ( record.temp.day * record.daySeconds + record.temp.night * record.nightSeconds ) / DAY_SECONDS
avgHelper = avgHelper + avgTemperature
end
avgTemperature = avgHelper / #t
return dz.utils.round(maxTemperature,1), dz.utils.round(minTemperature,1), dz.utils.round(avgTemperature,1)
end
local function getHoursTemperatures(t)
local maxTemperature, maxTemperatureToday, maxTemperatureTomorrow = -100, -100, -100
local minTemperature, minTemperatureToday, minTemperatureTomorrow = 100, 100, 100
local avgHelper = 0
for _, record in ipairs(t.hourly) do
if record.dt < endOfToday and record.temp > maxTemperatureToday then maxTemperatureToday = record.temp end
if record.dt < endOfToday and record.temp < minTemperatureToday then minTemperatureToday = record.temp end
if record.dt > endOfToday and record.dt < endOfTomorrow and record.temp > maxTemperatureTomorrow then maxTemperatureTomorrow = record.temp end
if record.dt > endOfToday and record.dt < endOfTomorrow and record.temp < minTemperatureTomorrow then minTemperatureTomorrow = record.temp end
if record.temp > maxTemperature then maxTemperature = record.temp end
if record.temp < minTemperature then minTemperature = record.temp end
avgHelper = avgHelper + record.temp
end
t.maxTemperature48Hours = dz.utils.round(maxTemperature,1)
t.minTemperature48Hours = dz.utils.round(minTemperature,1)
t.avgTemperature48Hours = dz.utils.round((avgHelper / #t.hourly),1)
t.maxTemperatureToday = dz.utils.round(maxTemperatureToday,1)
t.maxTemperatureTomorrow = dz.utils.round(maxTemperatureTomorrow,1)
t.minTemperatureToday = dz.utils.round(minTemperatureToday,1)
t.minTemperatureTomorrow = dz.utils.round(minTemperatureTomorrow,1)
return t
end
local function getHoursRain(t)
if t == nil then return 0 end
local totalmm,todaymm, tomorrowmm = 0,0,0
for key, record in ipairs(t) do
if type(record) == 'table' and record.rain ~= nil then
for id, mm in pairs(record.rain) do
todaymm = todaymm + ( ( record.dt < endOfToday and mm ) or 0 )
tomorrowmm = tomorrowmm + ( ( record.dt > endOfToday and record.dt < endOfTomorrow and mm ) or 0 )
totalmm = totalmm + mm
end
end
end
return totalmm, todaymm, tomorrowmm
end
local function get8DaysRain(t)
if t == nil then return 0 end
local totalmm = 0
for key, record in ipairs(t) do
if type(record) == 'table' and record.rain ~= nil then
totalmm = totalmm + record.rain
end
end
return totalmm
end
local function createResultTable(rt)
rt.currentRain = getCurrentRain(rt.current.rain)
rt.rain48Hours, rt.todaymm, rt.tomorrowmm = getHoursRain(rt.hourly)
rt.rain8Days = get8DaysRain(rt.daily)
rt = getHoursTemperatures(rt)
rt.maxTemperature8Days, rt.minTemperature8Days, rt.avgTemperature8Days = get8DaysTemperatures(rt.daily)
rt.maxTemperature8Days = math.max(rt.maxTemperature48Hours, rt.maxTemperature8Days)
rt.minTemperature8Days = math.min(rt.minTemperature48Hours, rt.minTemperature8Days)
end
local function updateTextDevice(t)
local text = 'range: rain - max Temp' .. '\n'
text = text .. 'current: ' .. t.currentRain .. ' mm, ' .. t.current.temp .. ' °C' .. '\n'
text = text .. '48 hours: ' .. t.rain48Hours .. ' mm, ' .. t.maxTemperature48Hours .. ' °C' .. '\n'
text = text .. '8 days: ' .. t.rain8Days .. ' mm, ' .. t.maxTemperature8Days .. ' °C' .. '\n'
dv.weatherText.updateText(text)
end
local function deviceExists(dv)
if dv == nil then
return false
else
return dz.utils.deviceExists(dv.name)
end
end
local function updateDevice(dv, value)
if deviceExists(dv) then
if dv.deviceType == 'Temp' then
dv.updateTemperature(value)
else
dv.updateCustomSensor(value)
end
end
end
local function updateDevices(rt)
updateDevice(dv.currentRain, rt.currentRain)
updateDevice(dv.rain48Hours, rt.rain48Hours)
updateDevice(dv.rain8Days, rt.rain8Days)
updateDevice(dv.rainTomorrow, rt.tomorrowmm)
updateDevice(dv.currentTemperature, rt.current.temp)
updateDevice(dv.maxTemperature48Hours, rt.maxTemperature48Hours)
updateDevice(dv.maxTemperature8Days, rt.maxTemperature8Days)
updateDevice(dv.maxTemperatureToday, rt.maxTemperatureToday)
updateDevice(dv.minTemperatureToday, rt.minTemperatureToday)
updateDevice(dv.maxTemperatureTomorrow, rt.maxTemperatureTomorrow)
updateDevice(dv.minTemperatureTomorrow, rt.minTemperatureTomorrow)
updateDevice(dv.minTemperature48Hours, rt.minTemperature48Hours)
updateDevice(dv.minTemperature8Days, rt.minTemperature8Days)
updateDevice(dv.avgTemperature48Hours, rt.avgTemperature48Hours)
updateDevice(dv.avgTemperature8Days, rt.avgTemperature8Days)
if deviceExists(dv.rainToday) and dv.rainToday.lastUpdate.dDate < ( endOfToday - DAY_SECONDS ) then
updateDevice(dv.rainToday, rt.todaymm)
elseif deviceExists(dv.rainToday) then
updateDevice(dv.rainToday, math.max(rt.todaymm, tonumber(dv.rainToday.sValue)))
end
if deviceExists(dv.maxTemperatureToday) and dv.maxTemperatureToday.lastUpdate.dDate < ( endOfToday - DAY_SECONDS ) then
updateDevice(dv.maxTemperatureToday, rt.maxTemperatureToday)
elseif deviceExists(dv.maxTemperatureToday) then
updateDevice(dv.maxTemperatureToday, math.max(rt.maxTemperatureToday, dv.maxTemperatureToday.temperature))
end
if deviceExists(dv.weatherText) then updateTextDevice(rt) end
end
-- main code
if item.isHTTPResponse then
if item.ok and item.isJSON then
local rt = item.json
createResultTable(rt)
updateDevices(rt)
else
dz.log('There was a problem with the response from ' .. scriptVar, dz.LOG_ERROR)
dz.log(item, dz.LOG_DEBUG)
end
else -- get the data from openweatherMap
dz.openURL({
url = openWeatherURL ..
'&lat=' .. dz.settings.location.latitude ..
'&lon=' .. dz.settings.location.longitude ..
'&appid=' .. openWeatherAPIKey,
callback = scriptVar, -- see httpResponses above.
})
end
end
}