Expansion and improvements of the advice. Major changes in the scripts.
The reason for this was the fact that the wind speed forecast was missing and therefore no advice for the coming period could be calculated.
Finally I added and changed a lot:
- All data from the same source (Open Meteo, Normal use is free for nun-commercial use without api-key.)
- Local calculation of the LKI. (Open Meteo gives the components, but not the index as used in the calculation of the advice.)
- Get also wind speed forcast for the same number of hours from Open Meteo.
- Therefore a custom wind device shoud be created.
- Having all the forcast data makes it possible to calculate the advice based on the average of the predicted LKI and wind speed.
- The number of hours for the forecast is made adjustable between 1 and 72 via setpoint.
- Therefore a custom setpoint device must also be created.
- The maximum hour limit can also be set dynamically until 00:00 hour by a variable in the script.
- Give color to the text of current advice and the forcast advice in accordance with the code.
- Give up~ and downarrows with color to increasing or decresing values. Red is worse, green is better.
More extensive:
The forecast of only the air quality for the next few hours was of limited value. It doesn't mean much, if you have no idea how the wind speed will develop in that same time. With Buienradar I only get the current wind speed for my location.
I was looking for a wind forecast. I found this in the form of the free weather api from
Open-Meteo. 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).
There, also based on your own coordinates, data can be retrieved, including wind speed.
It can include both the current values and the expectation for several days.
Although there is a difference with the Buienradar, I decided to get the wind speed forecast from this source. I cannot explain the difference, nor determine which of the two is better. And then there is the fact that Buienradar does not give me future hours via the plug-in.
Now I am able to calculate the average future wind speed. Subsequently, the existing calculation on the average LKI and the average wind speed can be used to give a forecast recommendation.
To use the same source for the current and expected values, I have added a new custom wind device. The new script retrieves both current values and expected wind speed from open-meteo.
Then I decided to take everyting from the same source. Also the parameters for the air quality and calculate the current and forcast air quality index myself. This index can be calculated from Nitrogen dioxide, Ozone, PM10 and PM2,5 levels.
By using the average, you don't take into account intermediate fluctuations, but you do not turn your fireplace on and off every hour. So this average seemed to me to be an acceptable solution, which at least gives a better idea of the coming period.
To make it possible to easily change the number of hours for the advice, I created a setpoint device.
Changing the value triggers the script. You can set the limits of this device from 1 to 72 hour.
Then I realized that you normally check during the day to see if you can light the fireplace today. In that case it is sufficient to have advice that does not go beyond midnight. By setting a variable you can ensure that the setpoint is set dynamically until midnight. It counts down during the day. In this case it has no effect to change the setpoint manually.
The result looks like this:

- LocaleStookwijzer+Setpoint-JanPep.png (27.92 KiB) Viewed 3862 times
I made the following adjustments to achieve this:
(Please note I sometimes mess around English and Dutch names a bit, but for the sake of convenience I will here stick to the names I use. Call it what you want. Also compared to the earlier scripts, I have renamed a few things.)
Steps to take:
First you need:
- Custom Setpoint device (with unit = hour, step = 1, min = 1, max = 72)
- Custom Sensor (with Ax-label LKI)
- Custom Alert device for the advice
- Custom Wind device (Wind+Temp+Chill is used!)
-
Put the index numbers of your devices in the beginning of the scripts.
1.
I
disabled the original t-Luchtkwaliteitindex script which took LKI from
https://api.luchtmeetnet.nl
2.
I created a new more complex script
dt-OpenMeteo which uses multiple callbacks.
It became a huge script, but it runs very fast. With testing the whole procedure with two api calls, all the callculations and updating the devices took about 700 ms.
- Script is triggered via the timer 'at *:02' or by changing the setpoint.
- The setpoint defines the forcastHourLimit if the script is not set to dynamic.
- At first start it gets the used air quallity parameters for the requested number of hours.
- API is called for 1, 2 or 3 days (data from 00:00 - 23:00). This gives you an absolute maximum of 72 hours.
- In the first callback it calculates the current LKI and the forcast LKI for the set number of hours.
- The LKI device is updated and the average of the coming hours is stored in a global_data variable.
- Then it calls (other api) for the wind data for the same number of hours.
- In the second callback it calculates the average wind speed prediction.
- The wind device is updated for the current hour and the average of the coming hours is stored in a global_data variable.
- When set to dynamic, the setpoint will be updated for the remaining number of hours of the day.
- This update of the setpoint will again trigger the script, but will be ignored and do nothing by break when set to dynamic.
- Update of the wind device in its turn triggers the script d-StokwijzerLokaal, which generates the advice.
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 = 1, the forcast 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 forcast ozone, pm10, pm2_5, nitrogen_dioxide data for current location
-- Calculates LKI for current time and average LKI for the forcastHours.
-- 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 = 1.
-- Works with _d.forcastHours, _d.avgForcastLKI and _d.avgForcastwindspeed from global_data
--- Your settings ----------------------------------------------------------------
-- First set the used device index numbers and variables you might want to change.
local meteolki_idx = n -- idx of your custom sensor 'Luchtkwaliteitsindex'.
local setpoint_idx = n -- idx of your custom setpoint device.
local meteowind_idx = n -- idx of your custom meteowind sensor
local useDynamicSetpoint = 0 -- 1 = Limit forcast until 00:00 hour. 0 = Use Setpoint.
-- loging level 0 = NO logging, 1 = INFO, 2 = DEBUG, 3 = ERROR or 4 = FORCE
local debug_level = 0
----------------------------------------------------------------------------------
return {
on = {
timer = {
--'every 2 minutes', -- Only used for testing.
'at *:02', -- (LKI info online is updated at the hour)
},
devices = {
setpoint_idx, -- idx of your setpoint for wanted forcastHours
},
httpResponses = {
'meteoair', -- matches callback string below
'meteowind' -- matches callback string below
},
},
logging = {
level = domoticz.LOG_INFO,
marker = 'OpenMeteo-',
},
execute = function(domoticz, triggeredItem)
-- Set Local environment=================
local _u = domoticz.utils -- Holds subset of handy utilities.
local _h = domoticz.helpers -- Holds the global functions.
local _d = domoticz.globalData -- Holds the global data.
-- Get location coordinates
local lat = domoticz.settings.location.latitude
local long = domoticz.settings.location.longitude
-- currenTime = RawDateTime = 2022-12-06 19:50:00
local currentTime = tostring(domoticz.time.rawDateTime)
-- resultTime comes back in json result e.g. "2024-03-26T00:00"
local resultTime = domoticz.time.dateToDate( currentTime, 'yyyy-mm-dd hh:MM:ss', 'yyyy-mm-ddThh:00', 0 )
local nextHour = domoticz.time.dateToDate( currentTime, 'yyyy-mm-dd hh:MM:ss', 'hh', 3600 )
-- For air and Wind data
local pm10 = 0
local pm2_5 = 0
local nitrogen_dioxide = 0
local ozone = 0
local lki = 0
local totallkiValues = 0 -- Will hold a count of the total of the values we get.
local intloopCounter = 0 -- Will hold a count of the the number of the values we get.
local meteowindSpeed = domoticz.devices(meteowind_idx).speedMs -- Gets last stored value
local totalwindspeedValues = 0 -- Will hold a count of the total of the values we get.
-- For forcastHours calculation
local forcastDays = 2 --2 is default
local forcastHoursLimit = domoticz.devices(setpoint_idx).setPoint
if useDynamicSetpoint == 1 then
-- Calculate remaining hous untill (including) 00:00 o'clock.
forcastHoursLimit = tonumber( 25 - nextHour )
end
if nextHour + forcastHoursLimit >= 48 then forcastDays = 3 end
if nextHour + forcastHoursLimit < 48 then forcastDays = 2 end
if nextHour + forcastHoursLimit < 24 then forcastDays = 1 end
-- Local Functions go here =============
--To calculate the LKI index.
local function calculatedLKI( ozone, pm10, pm2_5, nitrogen_dioxide )
lki = 0
-- 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
if ozone > 0 and ozone <=15 then if lki < 1 then lki = 1 end end
if pm10 > 0 and pm10 <= 10 then if lki < 1 then lki = 1 end end
if pm2_5 > 0 and pm2_5 <= 10 then if lki < 1 then lki = 1 end end
if nitrogen_dioxide > 0 and nitrogen_dioxide <= 10 then if lki < 1 then lki = 1 end end
--_h.logthis( domoticz, debug_level, 'LKI stap 1 = ' .. lki )
if ozone > 15 and ozone <= 30 then if lki < 2 then lki = 2 end end
if pm10 > 10 and pm10 <= 20 then if lki < 2 then lki = 2 end end
if pm2_5 > 10 and pm2_5 <= 15 then if lki < 2 then lki = 2 end end
if nitrogen_dioxide > 10 and nitrogen_dioxide <= 20 then if lki < 2 then lki = 2 end end
--_h.logthis( domoticz, debug_level, 'LKI stap 2 = ' .. lki )
if ozone > 30 and ozone <= 40 then if lki < 3 then lki = 3 end end
if pm10 > 20 and pm10 <= 30 then if lki < 3 then lki = 3 end end
if pm2_5 > 15 and pm2_5 <= 20 then if lki < 3 then lki = 3 end end
if nitrogen_dioxide > 20 and nitrogen_dioxide <= 30 then if lki < 3 then lki = 3 end end
--_h.logthis( domoticz, debug_level, 'LKI stap 3 = ' .. lki )
if ozone > 40 and ozone <= 60 then if lki < 4 then lki = 4 end end
if pm10 > 30 and pm10 <= 45 then if lki < 4 then lki = 4 end end
if pm2_5 > 20 and pm2_5 <= 30 then if lki < 4 then lki = 4 end end
if nitrogen_dioxide > 30 and nitrogen_dioxide <= 45 then if lki < 4 then lki = 4 end end
--_h.logthis( domoticz, debug_level, 'LKI stap 4 = ' .. lki )
if ozone > 60 and ozone <= 80 then if lki < 5 then lki = 5 end end
if pm10 > 45 and pm10 <= 60 then if lki < 5 then lki = 5 end end
if pm2_5 > 30 and pm2_5 <= 40 then if lki < 5 then lki = 5 end end
if nitrogen_dioxide > 45 and nitrogen_dioxide <= 60 then if lki < 5 then lki = 5 end end
--_h.logthis( domoticz, debug_level, 'LKI stap 5 = ' .. lki )
if ozone > 80 and ozone <= 100 then if lki < 6 then lki = 6 end end
if pm10 > 60 and pm10 <= 75 then if lki < 6 then lki = 6 end end
if pm2_5 > 40 and pm2_5 <= 50 then if lki < 6 then lki = 6 end end
if nitrogen_dioxide > 60 and nitrogen_dioxide <= 75 then if lki < 6 then lki = 6 end end
--_h.logthis( domoticz, debug_level, 'LKI stap 6 = ' .. lki )
if ozone > 100 and ozone <= 140 then if lki < 7 then lki = 7 end end
if pm10 > 75 and pm10 <= 100 then if lki < 7 then lki = 7 end end
if pm2_5 > 50 and pm2_5 <= 70 then if lki < 7 then lki = 7 end end
if nitrogen_dioxide > 75 and nitrogen_dioxide <= 100 then if lki < 7 then lki = 7 end end
--_h.logthis( domoticz, debug_level, 'LKI stap 7 = ' .. lki )
if ozone > 140 and ozone <= 180 then if lki < 8 then lki = 8 end end
if pm10 > 100 and pm10 <= 125 then if lki < 8 then lki = 8 end end
if pm2_5 > 70 and pm2_5 <= 90 then if lki < 8 then lki = 8 end end
if nitrogen_dioxide > 100 and nitrogen_dioxide <= 125 then if lki < 8 then lki = 8 end end
--_h.logthis( domoticz, debug_level, 'LKI stap 8 = ' .. lki )
if ozone > 180 and ozone <= 200 then if lki < 9 then lki = 9 end end
if pm10 > 125 and pm10 <= 150 then if lki < 9 then lki = 9 end end
if pm2_5 > 90 and pm2_5 <= 100 then if lki < 9 then lki = 9 end end
if nitrogen_dioxide > 125 and nitrogen_dioxide <= 150 then if lki < 9 then lki = 9 end end
--_h.logthis( domoticz, debug_level, 'LKI stap 9 = ' .. lki )
if ozone > 200 then lki = 10 end
if pm10 > 150 then lki = 10 end
if pm2_5 > 100 then lki = 10 end
if nitrogen_dioxide > 150 then lki = 10 end
--_h.logthis( domoticz, debug_level, 'LKI stap 10 = ' .. lki )
return lki
end
-- To calculate the windDirection.
local function getWindDirectionString( windDirection )
local directionString = ''
-- When string is not in Enlish, the icon does not appear!
if windDirection >= 0 and windDirection < 11.25 then directionString = 'N' end
if windDirection >= 11.25 and windDirection < 33.75 then directionString = 'NNE' end
if windDirection >= 33.75 and windDirection < 56.25 then directionString = 'NE' end
if windDirection >= 56.25 and windDirection < 78.75 then directionString = 'ENE' end
if windDirection >= 78.75 and windDirection < 101.25 then directionString = 'E' end
if windDirection >= 101.25 and windDirection < 123.75 then directionString = 'ESE' end
if windDirection >= 123.75 and windDirection < 146.25 then directionString = 'SE' end
if windDirection >= 146.25 and windDirection < 168.75 then directionString = 'SSE' end
if windDirection >= 168.75 and windDirection < 191.25 then directionString = 'S' end
if windDirection >= 191.25 and windDirection < 213.75 then directionString = 'SSW' end
if windDirection >= 213.75 and windDirection < 236.25 then directionString = 'SW' end
if windDirection >= 236.25 and windDirection < 258.75 then directionString = 'WSW' end
if windDirection >= 258.75 and windDirection < 281.25 then directionString = 'W' end
if windDirection >= 281.25 and windDirection < 303.75 then directionString = 'WW' end
if windDirection >= 303.75 and windDirection < 326.25 then directionString = 'NW' end
if windDirection >= 326.25 and windDirection < 348.75 then directionString = 'NNW' end
if windDirection >= 348.75 and windDirection <= 360 then directionString = 'N' end
return directionString
end
-- Now start to do something ===========================
if ( triggeredItem.isDevice ) then
if useDynamicSetpoint == 1 then
_h.logthis( domoticz, debug_level, 'useDynamicSetpoint is ON. No trigger for change of ' .. domoticz.devices(setpoint_idx).name .. '.')
-- Do not respond to change of the setPoint.
return
end
end
-- Get the data for air.
if ( triggeredItem.isTimer or useDynamicSetpoint == 0 ) then
-- Log what we do.
_h.logthis( domoticz, debug_level, '#forcastDays to retreive is set to ' .. forcastDays )
_h.logthis( domoticz, debug_level, '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=' .. forcastDays)
-- Retrieve the data
domoticz.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=' .. forcastDays,
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
_h.logthis( domoticz, debug_level, 'Item and JSON - OK' )
-- We have some result. Store in table.
local result_table = triggeredItem.json
if type( result_table ) == "table" then
_h.logthis( domoticz, debug_level, 'result_table: type = ' .. type( result_table ) )
-- Now loop simultaneously trhough the hourly tables for the forcast
local tc = #result_table.hourly.time
for i = 1, tc do
-- Only take the future windspeed for the number of hours we have also for the LKI in _d.forcastHours.
if intloopCounter < 1 and result_table.hourly.time[i] == resultTime then
-- This is for the current hour.
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]
lki = calculatedLKI( ozone, pm10, pm2_5, nitrogen_dioxide )
_h.logthis( domoticz, debug_level, 'Current time = ' .. intloopCounter .. ' - time - value: ' .. result_table.hourly.time[i] .. ' - LKI = ' ..lki .. '; pm10 = ' .. pm10 .. '; pm2_5 = ' .. pm2_5 .. '; nitrogen_dioxide = ' .. nitrogen_dioxide ..'; ozone = ' .. ozone )
-- Update sensor with current LKI value
_h.logthis( domoticz, debug_level, 'Update ' .. domoticz.devices(meteolki_idx).name .. '.' )
domoticz.devices(meteolki_idx).updateCustomSensor(lki)
intloopCounter = intloopCounter + 1
end
if result_table.hourly.time[i] > resultTime and intloopCounter <= forcastHoursLimit then
-- This is for the forcast
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]
lki = calculatedLKI( ozone, pm10, pm2_5, nitrogen_dioxide )
-- Update totallkiValues with current LKI value
totallkiValues = totallkiValues + calculatedLKI( ozone, pm10, pm2_5, nitrogen_dioxide )
_h.logthis( domoticz, debug_level, 'Record = ' .. intloopCounter .. ' - time - value: ' .. result_table.hourly.time[i] .. ' - LKI = ' ..lki .. '; pm10 = ' .. pm10 .. '; pm2_5 = ' .. pm2_5 .. '; nitrogen_dioxide = ' .. nitrogen_dioxide ..'; ozone = ' .. ozone )
intloopCounter = intloopCounter + 1
end
end
-- Calculate and store the average value for future hours in global_data.
--NB. This will be used in script d-StookwijzerLokaal
-- Correct intloopCounter for last round
intloopCounter = intloopCounter -1
_d.avgForcastLKI = _u.round( totallkiValues / intloopCounter, 0 )
_h.logthis( domoticz, debug_level, 'Found average LKI = ' .. _d.avgForcastLKI .. ' in ' .. intloopCounter .. ' records.')
-- Keep track of the number of hours that is received.
_d.forcastHours = intloopCounter
else
_h.logthis( domoticz, debug_level, 'No result_table found' )
end
else
_h.logthis( domoticz, debug_level, 'Item or JSON - NOT OK' )
end
-- Get the data for Wind.
-- Log what we do.
_h.logthis( domoticz, debug_level, 'forcastDays is set to ' .. forcastDays )
_h.logthis( domoticz, debug_level, '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=' .. forcastDays )
-- Retrieve the data
domoticz.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=' .. forcastDays,
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
_h.logthis( domoticz, debug_level, 'Item and JSON - OK' )
-- We have some result. Store in table.
local result_table = triggeredItem.json
if type(result_table) == "table" then
_h.logthis( domoticz, debug_level, 'result_table: type = ' .. type(result_table) )
-- 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 = getWindDirectionString( windDirection )
local windGust = result_table.current.wind_gusts_10m
_h.logthis( domoticz, debug_level, 'Current values - temperature = ' .. temperature .. '; chill = ' .. chill .. '; windSpeedMs = ' .. windSpeedMs ..'; windDirection = ' .. windDirection .. '; windDirectionString = ' .. windDirectionString ..'; windGust = ' .. windGust )
intloopCounter = 1
local tc = #result_table.hourly.time
_h.logthis( domoticz, debug_level, 'Forcasthours = ' .. _d.forcastHours )
for i = 1, tc do
if result_table.hourly.time[i] > resultTime and intloopCounter <= _d.forcastHours then
_h.logthis( domoticz, debug_level, 'Record = ' .. intloopCounter .. ' - time - value: ' .. result_table.hourly.time[i] .. ' - ' .. result_table.hourly.wind_speed_10m[i])
-- Value for the current hour.
meteowindSpeed = result_table.hourly.wind_speed_10m[i]
totalwindspeedValues = totalwindspeedValues + meteowindSpeed
intloopCounter = intloopCounter + 1
end
end
-- Calculate and store the average value for future hours in global_data.
--NB. This will be used in script d-StookwijzerLokaal.
-- Correct intloopCounter for last round
intloopCounter = intloopCounter -1
_d.avgForcastwindspeed = _u.round( totalwindspeedValues / intloopCounter, 1 )
_h.logthis( domoticz, debug_level, 'Found average windspeed = ' .. _d.avgForcastwindspeed .. ' in ' .. intloopCounter .. ' records.')
-- Now update our meteowinddevice, which triggers d-StookwijzerLokaal script to produce the advice.
_h.logthis( domoticz, debug_level, 'Update ' .. domoticz.devices(meteowind_idx).name .. '.' )
domoticz.devices(meteowind_idx).updateWind( windDirection, windDirectionString, windSpeedMs, windGust, temperature, chill )
--Finally update the setpoint if dynamic mode is enabled.
if useDynamicSetpoint == 1 then
-- Update the setpoint dynamically.
_h.logthis( domoticz, debug_level, 'useDynamicSetpoint is ON. Update ' .. domoticz.devices(setpoint_idx).name .. ' to ' .. forcastHoursLimit .. '.' )
domoticz.devices(setpoint_idx).updateSetPoint( forcastHoursLimit )
end
else
_h.logthis( domoticz, debug_level, 'No result_table found' )
end
else
_h.logthis( domoticz, debug_level, 'Item or JSON - NOT OK' )
end
end
end
end
}
-- That's All --------------------------------------------------
3.
I modified
global_data
The average LKI and wind speed are stored here. They are called from scripts dt-OpenMeteo and d-Stokwijzerlokaal.
The logging function is also included in this central location, which is used from multiple scripts.
(Change or comment out the logging if it is not desired)
Code: Select all
-- 05-01-2023: Script created by Jan peppink, https://ict.peppink.nl
-- This scripts holds all the globally persistent variables and helper functions
-- See the documentation in the wiki
-- NOTE: THERE CAN BE ONLY ONE global_data SCRIPT in your Domoticz install.
-- 26-03-2024: renamed variables; added: avgForcastwindspeed
return {
-- Global persistent data
data = {
-- Used in t-Alarmeringen for Alarmeringen.nl.
lastAlarmNotification = {},
-- Used in t-OpenMeteo-Air, d-OpenMeteo-Wind and d-StookwijzerLokaal.
avgForcastLKI = { initial = 0 },
avgForcastwindspeed = { initial = 0 },
forcastHours = { initial = 0 },
},
-- Global helper functions
helpers = {
logthis = function( domoticz, debug_level, log_string )
-- Call this function with domoticz.helpers.logthis( domoticz, debug_level, 'string to log')
-- The function gives the option to log or not to log under certain conditions.
-- You then have only to set debug_level once in a script to change the logging level (or skip logging).
debug_level = debug_level or 0
if debug_level ~= 0 then
if debug_level == 1 then
log_level = domoticz.LOG_INFO
elseif debug_level == 2 then
log_level = domoticz.LOG_DEBUG
elseif debug_level == 3 then
log_level = domoticz.LOG_ERROR
elseif debug_level == 4
then log_level = domoticz.LOG_FORCE
else
return
end
-- Function. Creates a logging entry in the Domoticz log but respects the log level settings.
-- domoticz.LOG_INFO, domoticz.LOG_DEBUG, domoticz.LOG_ERROR or domoticz.LOG_FORCE
-- In Domoticz settings you can set the log level for dzVents.
-- For optional log_level the default is LOG_INFO
if log_string ~= "" then
-- domoticz.log(message, [level])
domoticz.log( log_string, log_level )
else
return
end
end
end
}
}
-- That's All --------------------------------------------------
4.
Many modifications to
d-StookwijzerLokaal
- Gets LKI and Wind speed from the custom devices to calculate an advice for current hour.
- Gets average LKI and average wind speed from the global_data variables to calculate forcast advice.
- Compose the text advices with color as described before.
- You may also want to be able to overrule the code orange (LKI = 5-7) advice, when there is enough wind. I added two variables for this purpose:
- pepsOverridemode (0 or 1) to toggle on and off.
- acceptableWindspeed, which you can set to the desired treshold that you find acceptable regardless of the LKI (between 5 and 7). I'm still experimenting with this and currently have it set to 5.5, which is between the definition of light and moderate wind.
- To make the advice a bit clearer and to limit the text, I have added the html color of the advice. (In this case, it's a pity that there is no blue alert icon to choose. I want to keep the color codes of the original definition. I thing they wanted to avoid giving it a green light

. )
- Up and down arrows indicate the future development of LKI and wind speed. Color of the arrows (red or green) indicate whether this better or worse). And the color of the text indicates which code applies to the prediction.
Code: Select all
-- 05-01-2023: Script created by Jan peppink, https://ict.peppink.nl
-- Based on information at: https://iplo.nl/thema/lucht/houtstook-stookwijzer-stookalert/#h80dfe1e2-a855-424b-81b4-cdd2bfe71123
-- Gives a treee line result with:
-- Alertcolor, Calculated code (in accordance with the definition on https://iplo.nl), current LKI.
-- Winddescription (Winddirection) windspeed in m/s, bft and km/h.
-- LKI expectation (average for number of coming hours that has been received.
-- NB. Expectation hours counts down when online information is not updatesd.
-- (remains future hours stored online, until we pass the end of it)
-- Depends on LKI device (= local Air Quality Index based on the coordinates).
-- Depends on wind device. (Local windspeed based on given coordinates).
-- Depends on stored average LKI for coming hours stored in global_data.
-- Getting alertLevel by number: 0=gray, 1=green, 2=yellow, 3=orange, 4=red
-- Setting alertLevel by constant: domoticz.ALERTLEVEL_GREY, ALERTLEVEL_GREEN, ALERTLEVEL_YELLOW, ALERTLEVEL_ORANGE, ALERTLEVEL_RED
-- 25-03-2024 added two variables to override toggle if and when you want to override Orange warning definition.
-- This is reasonable when LKI is 5, 6 or 7 and windspeed is acceptable.
-- Override is toggled by setting local pepsOverridemode = 1 (0 = off)
-- Acceptable windspeed can be set by local acceptableWindspeed = number
-- I call it 'Code blauw-PLUS' - The alertcolor remains green.
-- 26-03-2024 Improved advice based on windspeed forcast.
-- Depends on stored average Windspeed for coming hours stored in global_data.
-- Calculates forcast advice based on avg LKI and avg wind speed in coming hours.
--- Your settings ----------------------------------------------------------------
-- First set the used device index numbers and variables you might want to change.
local meteowind_idx = n --idx of your custom meteowind sensor, which triggers this script.
local meteolki_idx = n -- idx of your 'Luchtkwaliteitsindex' device
local alert_idx = n -- idx of the Local Stookwijzer sensor to store the advice.
-- Set overrule for situation where LKI between 5 and 6, but windspeed > acceptable windspeed
local pepsOverridemode = 1 -- 1 to enable the override. 0 to turn if off.
local acceptableWindspeed = 5.5 -- Adjust the limit for the wind speed to be used in override mode.
-- loging level 0 = NO logging, 1 = INFO, 2 = DEBUG, 3 = ERROR or 4 = FORCE
local debug_level = 0
----------------------------------------------------------------------------------
return {
on = {
devices = {
meteowind_idx, -- Update of meteowind, after update of LKI, triggers this script to run.
},
},
logging = {
level = domoticz.LOG_INFO,
marker = "LokaleStookwijzer-"
},
execute = function( domoticz )
-- Set Local environment=================
--local _u = domoticz.utils -- Holds subset of handy utilities.
local _h = domoticz.helpers -- Holds the global functions
local _d = domoticz.globalData -- Holds the global data
-- Get current LKI value
local lkiValue = domoticz.devices(meteolki_idx).sensorValue
-- Local Functions go here =============
-- Now start to do something ============
local windSpeedMs = domoticz.devices(meteowind_idx).speedMs -- Gets the current value needed for calculation
local windDirection = domoticz.devices(meteowind_idx).directionString -- just for information.
-- Translate abbreviation into Dutch abbreviation. E(ast) becomes O(ost) S(outh) becomes Z(uid).
windDirection = string.gsub(windDirection, "E", "O",2)
windDirection = string.gsub(windDirection, "S", "Z",2)
-- Cannot remember from where I got these naming definitions.
if windSpeedMs >= 0 and windSpeedMs <= 0.2 then windText = '\nWindstil (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔0 bft ↔ < 1 km/u' end
if windSpeedMs >= 0.3 and windSpeedMs <= 1.5 then windText = '\nZeer zwakke wind (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 1 bft↔ 1-5 km/u' end
if windSpeedMs >= 1.6 and windSpeedMs <= 3.3 then windText = '\nZwakke wind (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 2 bft ↔ 6-11 km/u' end
if windSpeedMs >= 3.4 and windSpeedMs <= 5.4 then windText = '\nZeer matige wind (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 3 bft ↔ 12-19 km/u' end
if windSpeedMs >= 5.5 and windSpeedMs <= 7.9 then windText = '\nMatige wind (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 4 bft ↔ 20-28 km/u' end
if windSpeedMs >= 8.0 and windSpeedMs <= 10.7 then windText = '\nVrij krachtige wind (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 5 bft ↔ 29-38 km/u' end
if windSpeedMs >= 10.8 and windSpeedMs <= 13.8 then windText = '\nKrachtige wind (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 6 bft ↔ 39-49 km/u' end
if windSpeedMs >= 13.9 and windSpeedMs <= 17.1 then windText = '\nHarde wind (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 7 bft ↔ 50-61 km/u' end
if windSpeedMs >= 17.2 and windSpeedMs <= 20.7 then windText = '\nStormachtige wind (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 8 bft ↔ 62-74 km/u' end
if windSpeedMs >= 20.8 and windSpeedMs <= 24.4 then windText = '\nStorm (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 9 bft ↔ 75-88 km/u' end
if windSpeedMs >= 24.5 and windSpeedMs <= 28.4 then windText = '\nZware storm (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 10 bft ↔ 89-102 km/u' end
if windSpeedMs >= 28.5 and windSpeedMs <= 32.6 then windText = '\nZeer zware storm (' .. windDirection .. ') ' .. windSpeedMs .. 'm/s ↔ 11 bft ↔ 103-117 km/u' end
if windSpeedMs > 32.6 then windText = '\nOrkaan (' .. windDirection .. ') ' .. windSpeedMs .. ' m/s ↔ 12 bft ↔ >117 km/u' end
-- Prepare the forecast prefix sign
-- Set the sign for expected increase or decrease. arrowup = ↑ arrowdown = ↓ equal = =
local lkiSign = '<strong><span style="color: #CC00CC;">↔</span></strong>'
if _d.avgForcastLKI > lkiValue then
lkiSign = '<strong><span style="color: #ff0000;">▲</span></strong>'
elseif _d.avgForcastLKI < lkiValue then
lkiSign = '<strong><span style="color: #228B22;">▼</span></strong>'
end
local windSign = '<strong><span style="color: #CC00CC;">↔</span></strong>'
if _d.avgForcastwindspeed > windSpeedMs then
windSign = '<strong><span style="color: #228B22;">▲</span></strong>'
elseif _d.avgForcastwindspeed < windSpeedMs then
windSign = '<strong><span style="color: #ff0000;">▼</span></strong>'
end
-- Calculate and set the Current code.
if _d.forcastHours == 0 and _d.avgForcastLKI == 0 then
--Code = Geel; No LKI data for current hour received.
alertText = 'Niet beschikbaar!! Laatste LKI was ' .. lkiValue .. windText
alertLevel = domoticz.ALERTLEVEL_YELLOW
elseif lkiValue <= 4 and windSpeedMs > 2 then
--Code = Blauw; You can light the fireplace, but take the neighbors into account.
--LKI <= 4 and windsspeed > 2m/sec.
alertText = '<span style="color: #0000FF;">Code blauw.</span> LKI: ' .. lkiValue .. windText
alertLevel = domoticz.ALERTLEVEL_GREEN
elseif lkiValue >= 5 and lkiValue <=7 and windSpeedMs > 2 then
--Code = Oranje; It is better not to burn wood now.
--LKI >= 5 and <= 7 and windspeed > 2m/sec.
alertText = '<span style="color: #FFA500;">Code oranje.</span> LKI: ' .. lkiValue .. windText
alertLevel = domoticz.ALERTLEVEL_ORANGE
elseif lkiValue >= 8 or windSpeedMs <= 2 then
--Code = Rood; Do not burn wood.
--LKI >= 8 and/or the windspeed is low (<= 2m/sec).
alertText = '<span style="color: #ff0000;">Code rood.</span> LKI: ' .. lkiValue .. windText
alertLevel = domoticz.ALERTLEVEL_RED
end
-- Set the forcast color
local htmlColor = '#000000;' --Black
if _d.avgForcastLKI <= 4 and _d.avgForcastwindspeed > 2 then
htmlColor = '#0000FF;' -- Blue
elseif _d.avgForcastLKI >= 5 and _d.avgForcastLKI <= 7 and _d.avgForcastwindspeed > 2 then
htmlColor = '#FFA500;' -- Orange
elseif _d.avgForcastLKI >= 8 or _d.avgForcastwindspeed <= 2 then
htmlColor = '#ff0000;' -- Red
end
-- You may want to override the code orange, when there is a certain wind speed.
if pepsOverridemode == 1 then
-- For current advice.
if lkiValue >= 5 and lkiValue <=7 and windSpeedMs >= acceptableWindspeed then
-- Added personal level Blauw-PLUS. LKI is higher, but there is wind.
--Code = Blauw; You can light the fireplace, but take the neighbors into account.
alertText = '<span style="color: #0000FF;">Code blauw-PLUS.</span> LKI: ' .. lkiValue .. windText
alertLevel = domoticz.ALERTLEVEL_GREEN
end
-- For the forecast advice color.
if _d.avgForcastLKI >= 5 and _d.avgForcastLKI <= 7 and _d.avgForcastwindspeed >= acceptableWindspeed then
htmlColor = 'blue'
end
end
-- Generate the new forecast.
alertText = alertText .. '\n<span style="color: ' .. htmlColor .. '"> x̄ verwachting ' .. _d.forcastHours .. ' uur </span>: LKI' .. lkiSign .. _d.avgForcastLKI .. ' en wind' .. windSign .. _d.avgForcastwindspeed .. ' m/s'
-- Now finally update with new alertLevel and alertText we have found.
_h.logthis( domoticz, debug_level, 'Update ' .. domoticz.devices(alert_idx).name .. '.' )
domoticz.devices(alert_idx).updateAlertSensor(alertLevel, alertText)
end
}
-- That's All --------------------------------------------------
Result with dynamic setpoint (Time =- 21:02 hr.)
Note the code blauw-
PLUS, which is caused by setting pepsOverridemode =1.
Normally LKI of 5 would give code Oranje. Now with Wind > 5.5 m/s it becomes blauw-Plus.

- LocaleStookwijzer+Setpoint2-JanPep.png (28.59 KiB) Viewed 3861 times
### That's all ###
Dz on Ubuntu VM on DS718+ behind FRITZ!Box.
EvoHome; MELCloud; P1 meter; Z-Stick GEN5; Z-Wave-js-ui; Sonoff USB-Dongle Plus-E; Zigbee2Mqtt; MQTT; Greenwave powernodes 1+6; Fibaro switch, plugs, smoke; FRITZ!DECT 200. Scripts listed in profile interests.