Code: Select all
-- 26-03-2024: Script created by Jan Peppink, https://ict.peppink.nl
-- Get data from https://open-meteo.com/
-- Free and without api key for non-commercial use and less than 10.000 daily API calls per day. (See other terms of use on their website).
-- Created URLs are visible in log when debug_level is set.
-- Script is triggered every hour on timer setting and also on change of the setpoint device (wanted #hours).
-- Setpoint can be set between 1 and 72 hour.
-- When you set useDynamicSetpoint = true, the forecast is calculated until 00:00 hour and the setpoint dynamically counts down.
-- In ths case manually changing the Setpoint has no effect and will be overwritten next round.
-- Gets data in two rounds with multiple callbacks, because there is a separate api for weater(wind) and for airquality.
-- 1. Gets the current and forecast ozone, pm10, pm2_5, nitrogen_dioxide data for current location
-- Calculates LKI for current time and average LKI for the forecastHours.
-- Saves LKI value in LKI device and average in global_data
-- 2. Gets the current wind data and forecast wind speed for current location.
-- Saves wind values in wind device and average in global_data.
-- Updates setpoint device with current value, when useDynamicSetpoint = true.
-- Works with _d.forecastHours, _d.avgforecastLKI and _d.avgforecastwindspeed from global_data
-- Added option to useDynamicSetpoint until midnight. This overrules manually set Setpoint and also ignores device trigger.
-- 07-04-2024: Changed forecastHours calculation. Fix for 23 hr. when useDynamicSetpoint = true to set hoursLimit = 1.
-- 10-04-2024: Fixed bug that caused a loop when triggered on device and useDynamicSetpoint == false.
-- 25-05-2024 Removed function 'getWindDirectionString'. Instead call _h.getDirectionfromDegree in global_data
-- 29-05-2024 Directly make use of dz.log instead of the 'logthis' function in global_data.
-- 13-02-2025 Changed use of the setpoint device with the use of Dynamic setpoint.
-- A. To use only the setpoint device, set useDynamicSetpoint to false
-- B. When useDynamicSetpoint = true, it calculates remaining hours until end of the day running by timer, BUT
-- C. Now can also directly be triggered when changing the setpoint(, while next hour continues with the dynamic setting).
-- Added useSetpointFor variable with:
-- Set to 'average' for calculation of average LKI, Windspeed and advice code UNTIL the setpoint time. (as it was before)
-- Set to 'value' to show the LKI, Windspeed and advice code AT the setpoint time. Makes use of _d.sa_forecastSetpointTable in global data.
-- 15-02-2025 Added optional colored footer showing forecast code color. (useColoredFooter = true or false)
-- footerHours defines how many hours you want to see in the footer. Min. = 1, Max = 72 ( disabled by 0). suggested 24.
-- The desired number of hours in the future (for setopint or footer) will be automatically adjusted when not available.
-- The maximum you can get is 72 minus the hours that have already passed today, where 00:00 hr. is the first.
-- Added _d.forecastColorTable (in global_data) used in d-StookwijzerLokaal.
-- Huge changes in the loops to take care for these settings.
-- Many variables renamed to better fit with the drastic changes.
-- Added useSetpointForFooter overrules footerHours and use the setpoint also for the footer. Same (once) when triggerd by the setpoint change manually.
-- Take care with LOG_DEBUG. complete tables are logged multiple times to keep track of the data changes.
-- I have shortened the lki calculation, which is frequently performed.
-- Changed the timer schedule not to run at night.
--- Your settings ----------------------------------------------------------------
-- First set the used device index numbers and variables you might want to change.
local meteolki_idx = 99999 -- idx of your custom sensor 'Luchtkwaliteitsindex'.
local meteolki2_5_idx = 99999 -- idx of your custom sensor 'Luchtkwaliteitsindex fijnstof'
local meteowind_idx = 99999 -- idx of your custom meteowind sensor
local sa_setpoint_idx = 9999 -- idx of your custom setpoint device.
local useDynamicSetpoint = true -- True = Limit forecast until 00:00 hour and use setpoint when changing. False = Use Setpoint only.
local useSetpointFor = 'average' -- set to 'average' or 'value'
local useColoredFooter = true -- false = Off; true = On. To show colored footer for next footerHours hours.
local footerHours = 24 -- (Coded here) How many hours forecast in footer.(showing <=24 = value per hour, 25-48 = avg per 2 hour, 49-72 = avg per 3 hour.)
local useSetpointForFooter = false -- Overrule footerHours and use the setpoint also for the footer.
local useNewRIVMcriteria = true -- Use the new criteria found on https://iplo.nl/thema/lucht/houtstook-stookwijzer/
-- and https://www.rivm.nl/nieuws/nieuwe-criteria-stookwijzer-op-basis-van-overlast
----------------------------------------------------------------------------------
return {
on = {
timer = {
--'every 2 minutes', -- Only used for testing.
'at *:02 between 8:00 and 23:04', -- (LKI info online is updated at the hour)
},
devices = {
sa_setpoint_idx, -- idx of your setpoint for wanted forecastHours
},
httpResponses = {
'meteoair', -- matches callback string below
'meteowind' -- matches callback string below
},
},
logging = {
-- Level can be domoticz.LOG_INFO, domoicz.LOG_MODULE_EXEC_INFO, domoticz.LOG_DEBUG, domoticz.LOG_ERROR or domoticz.LOG_FORCE
-- Level can be domoticz.LOG_INFO, domoticz.LOG_STATUS, domoticz.LOG_ERROR or domoticz.LOG_DEBUG
level = domoticz.LOG_STATUS,
--level = domoticz.LOG_DEBUG,
marker = 'OpenMeteo-',
},
execute = function( dz, triggeredItem )
-- Set Local environment=================
local _u = dz.utils -- Holds subset of handy utilities.
local _h = dz.helpers -- Holds the global functions.
local _d = dz.globalData -- Holds the global data.
-- Get location coordinates
local lat = dz.settings.location.latitude
local long = dz.settings.location.longitude
-- currenTime = rawDateTime = e.g. 2022-12-06 19:50:00
local currentTime = tostring( dz.time.rawDateTime )
-- resultTime comes back in json result e.g. "2024-03-26T00:00"
local resultTime = dz.time.dateToDate( currentTime, 'yyyy-mm-dd hh:MM:ss', 'yyyy-mm-ddThh:00', 0 )
-- nextHour is the first hour after the currentTime.
local nextHour = dz.time.dateToDate( currentTime, 'yyyy-mm-dd hh:MM:ss', 'hh', 3600 )
dz.log( 'currentTime = ' .. currentTime .. ' and nextHour = ' .. nextHour, dz.LOG_DEBUG )
-- For air and Wind data
local pm10 = 0
local pm2_5 = 0
local nitrogen_dioxide = 0
local ozone = 0
local lki = 0
local lki2_5 = 0
local totallkiValues = 0 -- Will hold a count of the total of the values we get.
local loopCounter = 0 -- Will hold a count of the the number of the values we get.
local meteowindSpeed = dz.devices( meteowind_idx ).speedMs -- Gets last stored value.
local totalWindspeedValues = 0 -- Will hold a count of the total of the values we get.
-- Set this to global_data. It is used in d-StookwijzerLokaal
_d.sa_useNewRIVMcriteria = useNewRIVMcriteria
-- Use the setpoint value as the default for number of hours in future we want
-- to calculate average until, or the value at that time.
local setpointHours = _u.round( dz.devices( sa_setpoint_idx ).setPoint, 0 )
dz.log( 'Wanted setpointHours = ' .. setpointHours, dz.LOG_DEBUG )
-- Therefore we set hoursLimit to the setpointHours for the average calculation.
local hoursLimit = setpointHours
if useSetpointForFooter == true or dz.devices(sa_setpoint_idx).lastUpdate.minutesAgo < 1 then
--Overrule footerHours with the setpointHours
footerHours = setpointHours
end
-- When useDynamicSetpoint == true, we use that, EXCEPT when the setpoint is changed manually.
if useDynamicSetpoint == true and dz.devices(sa_setpoint_idx).lastUpdate.minutesAgo > 1 then
-- Calculate remaining hous untill (including) 00:00 o'clock, which is next day.
if dz.time.dateToDate( currentTime, 'yyyy-mm-dd hh:MM:ss', 'hh', 0 ) == '23' then
-- Exeption! nexthour '00' does not fit with forecastDays calculation below.
-- Cannot add '00' hr. Therefore set nextHour to 24, which is 1 hour from now.
nextHour = 24
hoursLimit = 1
dz.log( 'For dynamic setpoint calculation corrected nextHour to ' .. nextHour .. ' and set hoursLimit to ' ..hoursLimit, dz.LOG_DEBUG )
else
hoursLimit = _u.round( 25 - nextHour, 0 )
end
end
-- Check how many days we need to ask for. Using the max number of hours.
dz.log( 'Wanted footerHours = ' .. footerHours, dz.LOG_DEBUG )
-- Start getting the MAX number of hours we need for value at setpoint, average until setpoint or footertable.
local maxHours = math.max( hoursLimit, setpointHours, footerHours )
-- The result of query in number of days also contains the past hours of today.
-- Therefore we add them into the totalHours (= + nextHour -1 )
local totalHours = maxHours + nextHour
dz.log( 'Wanted totalHours to calculate days from = ' .. maxHours .. '+' .. nextHour .. '=' .. totalHours, dz.LOG_DEBUG )
-- Get number of days we need to query
local forecastDays = 0
if totalHours >= 48 then
forecastDays = 3
elseif totalHours >= 24 and totalHours < 48 then
forecastDays = 2
elseif totalHours < 24 then
forecastDays = 1
end
dz.log( 'forecastDays to retreive is set to ' .. forecastDays, dz.LOG_DEBUG )
-- Local Functions go here =============
local function calculatedLKI( ozone, pm10, pm2_5, nitrogen_dioxide )
--To calculate the LKI index. See https://www.rivm.nl/bibliotheek/rapporten/2014-0050.pdf (§6.4, Table 7)
-- The component that falls into the highest category determines the value of the index.
--INDEX 1 2 3 4 5 6 7 8 9 10
--ozone 0-15 15-30 30-40 40-60 60-80 80-100 100-140 140-180 180-200 >200
--pm10 0-10 10–20 20–30 30-45 45-60 60-75 75-100 100-125 125-150 >150
--pm2_5 0-10 10-15 15-20 20-30 30-40 40-50 50-70 70-90 90-100 >100
--nitrogen_dioxide 0-10 10-20 20-30 30-45 45-60 60-75 75-100 100-125 125-150 >150
lki = 0
lki2_5 = 0
dz.log( '_d.sa_useNewRIVMcriteria is set to ' .. tostring( _d.sa_useNewRIVMcriteria ), dz.LOG_DEBUG )
--if _d.sa_useNewRIVMcriteria == true then
if ( pm2_5 > 0 and pm2_5 <= 10 ) then lki2_5 = 1 end
if ( pm2_5 > 10 and pm2_5 <= 15 ) then lki2_5 = 2 end
if ( pm2_5 > 15 and pm2_5 <= 20 ) then lki2_5 = 3 end
if ( pm2_5 > 20 and pm2_5 <= 30 ) then lki2_5 = 4 end
if ( pm2_5 > 30 and pm2_5 <= 40 ) then lki2_5 = 5 end
if ( pm2_5 > 40 and pm2_5 <= 50 ) then lki2_5 = 6 end
if ( pm2_5 > 50 and pm2_5 <= 70 ) then lki2_5 = 7 end
if ( pm2_5 > 70 and pm2_5 <= 90 ) then lki2_5 = 8 end
if ( pm2_5 > 90 and pm2_5 <= 100 ) then lki2_5 = 9 end
if ( pm2_5 > 100 ) then lki2_5 = 10 end
--return lki2_5
--else
if ( ozone > 0 and ozone <=15 ) or ( pm10 > 0 and pm10 <= 10 ) or ( pm2_5 > 0 and pm2_5 <= 10 ) or ( nitrogen_dioxide > 0 and nitrogen_dioxide <= 10 ) then lki = 1 end
if ( ozone > 15 and ozone <= 30 ) or ( pm10 > 10 and pm10 <= 20 ) or ( pm2_5 > 10 and pm2_5 <= 15 ) or ( nitrogen_dioxide > 10 and nitrogen_dioxide <= 20 ) then lki = 2 end
if ( ozone > 30 and ozone <= 40 ) or ( pm10 > 20 and pm10 <= 30 ) or ( pm2_5 > 15 and pm2_5 <= 20 ) or ( nitrogen_dioxide > 20 and nitrogen_dioxide <= 30 ) then lki = 3 end
if ( ozone > 40 and ozone <= 60 ) or ( pm10 > 30 and pm10 <= 45 ) or ( pm2_5 > 20 and pm2_5 <= 30 ) or ( nitrogen_dioxide > 30 and nitrogen_dioxide <= 45 ) then lki = 4 end
if ( ozone > 60 and ozone <= 80 ) or ( pm10 > 45 and pm10 <= 60 ) or ( pm2_5 > 30 and pm2_5 <= 40 ) or ( nitrogen_dioxide > 45 and nitrogen_dioxide <= 60 ) then lki = 5 end
if ( ozone > 80 and ozone <= 100 ) or ( pm10 > 60 and pm10 <= 75 ) or ( pm2_5 > 40 and pm2_5 <= 50 ) or ( nitrogen_dioxide > 60 and nitrogen_dioxide <= 75 ) then lki = 6 end
if ( ozone > 100 and ozone <= 140 ) or ( pm10 > 75 and pm10 <= 100 ) or ( pm2_5 > 50 and pm2_5 <= 70 ) or ( nitrogen_dioxide > 75 and nitrogen_dioxide <= 100 ) then lki = 7 end
if ( ozone > 140 and ozone <= 180 ) or ( pm10 > 100 and pm10 <= 125 ) or ( pm2_5 > 70 and pm2_5 <= 90 ) or ( nitrogen_dioxide > 100 and nitrogen_dioxide <= 125 ) then lki = 8 end
if ( ozone > 180 and ozone <= 200 ) or ( pm10 > 125 and pm10 <= 150 ) or ( pm2_5 > 90 and pm2_5 <= 100 ) or ( nitrogen_dioxide > 125 and nitrogen_dioxide <= 150 ) then lki = 9 end
if ( ozone > 200 ) or ( pm10 > 150 ) or ( pm2_5 > 100 ) or ( nitrogen_dioxide > 150 ) then lki = 10 end
--return lki
--end
return lki, lki2_5
end
-- Now start to do something ===========================
-- Get the data for air quality.
if ( triggeredItem.isTimer or triggeredItem.isDevice ) then
-- Retrieve the data
dz.openURL({
url = 'https://air-quality-api.open-meteo.com/v1/air-quality?latitude=' .. lat .. '&longitude=' .. long .. '¤t=pm10,pm2_5,nitrogen_dioxide,ozone&hourly=pm10,pm2_5,nitrogen_dioxide,ozone&timezone=auto&forecast_days=' .. forecastDays,
method = 'GET',
callback = 'meteoair'
})
end
-- Process the obtained data.
if ( triggeredItem.isHTTPResponse ) then
-- For AIR ------------------------------------------
if triggeredItem.trigger == 'meteoair' then
-- Check the response and process the data.
if ( triggeredItem.ok and triggeredItem.isJSON ) then
dz.log( 'Item and JSON - OK', dz.LOG_DEBUG )
-- We have some result. Store in table.
local result_table = triggeredItem.json
if type( result_table ) == "table" then
dz.log( 'result_table: type = ' .. type( result_table ), dz.LOG_DEBUG )
dz.log( 'Number of records in result_table.hourly.time = ' .. #result_table.hourly.time, dz.LOG_DEBUG )
-- Always clear the forecastSetpointTable before we start filling it.
_d.sa_forecastSetpointTable = {
LKI = 0,
windSpeed = 0,
alertText = ''
}
-- Always clear the forecastTable before we start filling it.
_d.sa_forecastTable = {}
--Build the table with wanted number of rows we need and what we do have.
-- The max we can have in te future is 72 records minus the hours that has passed already today.
-- This is (#rows in table - nextHour) for the day in table starts at 00:00)
-- Now loop trhough the hourly table for the lki
local tc = #result_table.hourly.time
for i = 1, tc do
-- Get LKI for the current hour and store into the LKI device.
if loopCounter < 1 and result_table.hourly.time[i] == resultTime then
-- This is for the current hour in the result_table that starts at 00:00.
pm10 = result_table.hourly.pm10[i]
pm2_5 = result_table.hourly.pm2_5[i]
nitrogen_dioxide = result_table.hourly.nitrogen_dioxide[i]
ozone = result_table.hourly.ozone[i]
-- Value for the this hour.
--lki = calculatedLKI( ozone, pm10, pm2_5, nitrogen_dioxide )
lki, lki2_5 = calculatedLKI( ozone, pm10, pm2_5, nitrogen_dioxide )
if _d.sa_useNewRIVMcriteria == true then
dz.log( 'CurrentTime Record = ' .. loopCounter .. ' - time - value: ' .. result_table.hourly.time[i] .. ' - LKI2.5 = ' ..lki2_5 .. '; pm2_5 = ' .. pm2_5, dz.LOG_DEBUG )
else
dz.log( 'CurrentTime Record = ' .. loopCounter .. ' - time - value: ' .. result_table.hourly.time[i] .. ' - LKI = ' ..lki .. '; pm10 = ' .. pm10 .. '; pm2_5 = ' .. pm2_5 .. '; nitrogen_dioxide = ' .. nitrogen_dioxide ..'; ozone = ' .. ozone, dz.LOG_DEBUG )
end
-- Update sensor with current LKI value
dz.log( 'Update LKI device ' .. dz.devices( meteolki_idx ).name .. '.', dz.LOG_DEBUG )
dz.devices( meteolki_idx ).updateCustomSensor( lki )
dz.log( 'Update LKI PM2.5 device ' .. dz.devices( meteolki2_5_idx ).name .. '.', dz.LOG_DEBUG )
dz.devices( meteolki2_5_idx ).updateCustomSensor( lki2_5 )
loopCounter = loopCounter + 1
end
-- Now fill forecastTables
if result_table.hourly.time[i] > resultTime and loopCounter <= maxHours then
-- This is for the forecast, staqrting at one hour from now.
pm10 = result_table.hourly.pm10[i]
pm2_5 = result_table.hourly.pm2_5[i]
nitrogen_dioxide = result_table.hourly.nitrogen_dioxide[i]
ozone = result_table.hourly.ozone[i]
-- Value for the this hour.
--lki = calculatedLKI( ozone, pm10, pm2_5, nitrogen_dioxide )
lki, lki2_5 = calculatedLKI( ozone, pm10, pm2_5, nitrogen_dioxide )
if _d.sa_useNewRIVMcriteria == true then
dz.log( 'Record = ' .. loopCounter .. ' - time: ' .. result_table.hourly.time[i] .. ' - LKI2.5 = ' .. lki2_5 .. '; pm2_5 = ' .. pm2_5, dz.LOG_DEBUG )
-- Now we are using the new criteria, FROM NOW ON we use lki2_5 for lki
lki = lki2_5
else
dz.log( 'Record = ' .. loopCounter .. ' - time: ' .. result_table.hourly.time[i] .. ' - LKI = ' ..lki .. '; pm10 = ' .. pm10 .. '; pm2_5 = ' .. pm2_5 .. '; nitrogen_dioxide = ' .. nitrogen_dioxide ..'; ozone = ' .. ozone, dz.LOG_DEBUG )
end
-- When useSetpointFor == 'value', update _d.forecastSetpointTable with lki.
if useSetpointFor == 'value' and loopCounter == setpointHours then
_d.sa_forecastSetpointTable.LKI = lki
end
-- Update forcastTable with the calculated lki
local newRecord = {
LKI = lki,
windSpeed = 0,
inFooter = false,
alertColor = ''
}
table.insert( _d.sa_forecastTable, newRecord )
--_Mark the wanted number of records to show up in footer.
if useColoredFooter == true and loopCounter <= footerHours then
-- Use for Footer
_d.sa_forecastTable[loopCounter].inFooter = true
end
-- To calculate for average stop adding when (dynamic) setpoint is reached. (maxHours can be more for footer or value at setpoint.)
if loopCounter <= hoursLimit then
-- Update totallkiValues with current LKI value
totallkiValues = totallkiValues + lki
end
loopCounter = loopCounter + 1
end
end
-- Adjust the limit when the limit is greater than the number of records retreived,
if #_d.sa_forecastTable < hoursLimit then hoursLimit = #_d.sa_forecastTable end
-- Calculate and store the average value for future hours in global_data.
--NB. This will be used in script d-StookwijzerLokaal
_d.sa_avgForecastLKI = _u.round( totallkiValues / hoursLimit, 0 )
dz.log( 'Found average LKI = ' .. _d.sa_avgForecastLKI .. ' in ' .. hoursLimit .. ' records.', dz.LOG_DEBUG )
-- Keep track of the number of hours that is received and used.
_d.sa_forecastHours = hoursLimit
else
dz.log( 'No result_table found', dz.LOG_ERROR )
end
else
dz.log( 'Item or JSON - NOT OK', dz.LOG_ERROR )
end
-- Get the data for Wind.
-- Retrieve the data
dz.openURL({
url = 'https://api.open-meteo.com/v1/forecast?latitude=' .. lat .. '&longitude=' .. long .. '¤t=temperature_2m,apparent_temperature,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=wind_speed_10m&wind_speed_unit=ms&timezone=auto&forecast_days=' .. forecastDays,
method = 'GET',
callback = 'meteowind'
})
end -- meteoair
--====================================================
-- For WIND -----------------------------------------
if triggeredItem.trigger == 'meteowind' then
-- Check the response and process the data.
if ( triggeredItem.ok and triggeredItem.isJSON ) then
dz.log( 'Item and JSON - OK', dz.LOG_DEBUG )
-- We have some result. Store in table.
local result_table = triggeredItem.json
if type( result_table ) == "table" then
dz.log( 'result_table: type = ' .. type( result_table ), dz.LOG_DEBUG )
-- Get the current values
--"temperature_2m": 10.2,
--"apparent_temperature": 6.2,
--"wind_speed_10m": 4.71,
--"wind_direction_10m": 197,
--"wind_gusts_10m": 16.5
local temperature = result_table.current.temperature_2m
local chill = result_table.current.apparent_temperature
local windSpeedMs = _u.round( result_table.current.wind_speed_10m, 1 )
local windDirection = result_table.current.wind_direction_10m
local windDirectionString = _h.getDirectionfromDegree( windDirection )
local windGust = result_table.current.wind_gusts_10m
dz.log( 'Current values - temperature = ' .. temperature .. '; chill = ' .. chill .. '; windSpeedMs = ' .. windSpeedMs ..'; windDirection = ' .. windDirection .. '; windDirectionString = ' .. windDirectionString ..'; windGust = ' .. windGust, dz.LOG_DEBUG )
-- LOG current windSpeedMs with the last windSpeedMs from Weerlive.
local weerliveWindSpeed = dz.devices( 1026 ).speedMs -- Gets last stored value from WeerliveWind sensor.
_h.AppendStringToFile( dz, '/home/janpep/domoticz/scripts/WindLog.txt', currentTime .. '\t' .. weerliveWindSpeed .. '\t' .. windSpeedMs .. '\n' )
loopCounter = 1
local tc = #result_table.hourly.time
--dz.log( 'forecasthours = ' .. _d.sa_forecastHours, dz.LOG_DEBUG )
for i = 1, tc do
if result_table.hourly.time[i] > resultTime and loopCounter <= maxHours then
-- Value for the this hour.
meteowindSpeed = result_table.hourly.wind_speed_10m[i]
dz.log( 'Record = ' .. loopCounter .. ' - time: ' .. result_table.hourly.time[i] .. ' - Wind = ' .. meteowindSpeed, dz.LOG_DEBUG )
-- When useSetpointFor == 'value', update _d.forecastSetpointTable with lki.
if useSetpointFor == 'value' and loopCounter == setpointHours then
_d.sa_forecastSetpointTable.windSpeed = meteowindSpeed
end
-- Update forcastTable with the windspeed.
_d.sa_forecastTable[loopCounter].windSpeed = meteowindSpeed
--dz.log( 'forecastTable has LKI = ' .. _d.sa_forecastTable[loopCounter].LKI .. ' and wind = ' .. _d.sa_forecastTable[loopCounter].windSpeed , dz.LOG_DEBUG )
-- To calculate for average stop adding when (dynamic) setpoint is reached. (maxHours can be more for footer or value at setpoint.)
if loopCounter <= _d.sa_forecastHours then
totalWindspeedValues = totalWindspeedValues + meteowindSpeed
end
loopCounter = loopCounter + 1
end
end
-- Calculate and store the average value for future hours in global_data.
--NB. This will be used in script d-StookwijzerLokaal.
_d.sa_avgForecastWindspeed = _u.round( totalWindspeedValues / _d.sa_forecastHours, 1 )
dz.log( 'Found average windspeed = ' .. _d.sa_avgForecastWindspeed .. ' in ' .. _d.sa_forecastHours .. ' records.', dz.LOG_DEBUG )
-- Now update our meteowinddevice, which immediately triggers d-StookwijzerLokaal script to produce the advice.
dz.log( 'Update ' .. dz.devices( meteowind_idx ).name .. '.', dz.LOG_DEBUG )
dz.devices( meteowind_idx ).updateWind( windDirection, windDirectionString, windSpeedMs, windGust, temperature, chill )
else
dz.log( 'No result_table found', dz.LOG_ERROR )
end
else
dz.log( 'Item or JSON - NOT OK', dz.LOG_ERROR )
end
end
end
end
}
-- That's All --------------------------------------------------