Configurable 'KNMI Weerlive' script

In this subforum you can show projects you have made, or you are busy with. Please create your own topic.

Moderator: leecollings

janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Configurable 'KNMI Weerlive' script

Post by janpep »

I hereby share the first edition of my new weather project.
I used Buienradar plugin for the weather, Weerlive script (for weather alerts) and Open meteo script (for wind and air quality forecast parameters).
Weerlive also offers the local data that I obtain from Buienradar. So I could reduce the number of sources I use.
The advantage of this is also that I can customize it entirely to my own taste.

The result is a huge script, that runs quite quickly:

Code: Select all

2024-05-16 13:10:00.302 Status: dzVents: Info: Weerlive-: ------ Start internal script: t-Weerlive:, trigger: "every 10 minutes"
2024-05-16 13:10:00.304 Status: dzVents: Info: Weerlive-: ------ Finished t-Weerlive
2024-05-16 13:10:03.365 Status: dzVents: Info: Weerlive-: ------ Start internal script: t-Weerlive: HTTPResponse: "weerlive"
2024-05-16 13:10:03.386 Status: dzVents: Info: Weerlive-: ------ Finished t-Weerlive
So I expanded my Weerlive.nl weather alert and now it can fill the following devices with data:
(There is optional use of devices. You can skip wat you do not need by giving them a dummy idx of 99999 in the script.)
Weather and temperature devices:
Weerlive-1-JanPep.png
Weerlive-1-JanPep.png (71.42 KiB) Viewed 4036 times
Text and Alert devides.
Weerlive-2-JanPep.png
Weerlive-2-JanPep.png (44.44 KiB) Viewed 4036 times
The script can automatically change the (optional used and defined) custom icons. These icons correspond to the possible Weerlive 'image text'.
I still have to see how I can make the (optional) icons available.
Weerlive-3-JanPep.png
Weerlive-3-JanPep.png (93.18 KiB) Viewed 4036 times
Some remarks:
  • Explanation of the api can be found at weerlive.nl, where you also can obtain an api key.
  • You can do 300 calls per day. (In debug mode remaining number of calls is logged.)
  • In the script you can see what devices it needs.
  • The BARO device forcast uses the 'image' text that is found for tomorrow.
  • Optional use of custom icons. Activate by setting local wl_icons_installed = 1.
    NB. DOUBLE check function getIconNumber and correct your iconNumbers !!
    Tip. Add icons alphabetically. (use same image names as in function 'getIconNumber') Manually add the first of these to a device.
    To discover the CustomImage number run:

    Code: Select all

    https://yourIP:yourport/json.htm?type=command&param=getdevices&plan=0
    to see all device information and search for your first icon by its name. There you find the number "CustomImage :"
    Then in the script you can alphabetically update the iconNumbers in the function 'getIconNumber' with the numbers that follow
  • The Weatherforcast device gets the icon that comes with forcast tomorrow. May sometimes be a bit confusing with the text that is shown.
  • The Rainforcast device gets the icon rain or NO rain, when not expected < 24 hours.
  • The script translates mm rainfall to l/m2 which is easier to understand.
  • The script calculates expected sun hours given as zond_perc_dag of the time between sunrise and sunset to hh:MM.
  • An explanation about this can be found at KNMI
For the ToDo list:
  • Weerlive does not give the wind gust information. I have hardcoded this to 0 for the time being.
  • I noticed that under the tab Other, the device has a wordwrap. When added to dashboard this is not the case.
And finally the script itself:

Code: Select all

-- 12-05-2024 script by Jan Peppink, https://ict.peppink.nl
-- Started for weather alarm Expanded as renewed custom script instead of Buienradar.
-- Reduce the number of weather information sources.
-- 
-- Extra explaining comments and choices made ------------------------------------
-- Explanation of the api can be found at https://weerlive.nl/delen.php, where you also can obtain an api key.
-- Optional use of devices. You can skip them by giving them a dummy idx of 99999.
-- The BARO device forcast uses the 'image' text that is found for tomorrow.
-- WindGust is not yet part of weerlive api result. Hardcoded set to 0
-- Optional use of custom icons. Activate by setting wl_icons_installed = 1
--   NB. DOUBLE check function getIconNumber and correct your iconNumbers !!
-- The Weatherforcast device gets the icon that comes with forcast tomorrow.
--   May sometimes be a bit confusing with the text that is shown.
-- The Rainforcast device gets the icon rain or NO rain, when not expected < 24 hours.
-- The script translates mm rainfall to l/m2 which is easier to understand.
-- The script calculates expected sun hours given as zond_perc_dag of the time between sunrise and sunset to hh:MM.
-- An explanation about this can be found at https://www.knmi.nl/kennis-en-datacentrum/achtergrond/de-weersverwachting

--- Your settings ----------------------------------------------------------------
-- First set the used device index numbers and variables you might want to change.
---#################################################################
-- NB! To configure, set the devices you do NOT want to use to 99999
-- Easy to temporarily or permanently not use a device without rearanging the code.
---#################################################################
local wl_alarm_idx = 99999--idx of the custom Alert device for Weather alarm.
local wl_thb_idx = 99999--idx of the Temperature+humidity+barometer device adapter.
local wl_gtemp_idx = 99999--idx of the Temperature device adapter.
local wl_wind_idx = 99999--idx of the Wind device adapter.
local wl_zicht_idx = 99999--idx of the Visibility device.
local wl_gr_idx = 99999      --idx of the Global Solar radiation device adapter.
local wl_verw_idx = 99999--idx of the weatherforcast Text device.
local wl_rain_idx = 99999--idx of the Rain forcast Text device.
local wl_apikey = 'HereYourApiKey' -- Access for weerlive.nl with your apikey.
local wl_icons_installed = 0    -- 1 or 0 when custom icons have been installed or not.

----------------------------------------------------------------------------------
return {
	on = {
		timer = { 
            --'every 2 minutes',  -- Only for testing.
            'every 10 minutes', 
        },
        httpResponses = {
		    'weerlive'       -- 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 = domoticz.LOG_INFO,
        --level = domoticz.LOG_DEBUG,
		marker = 'Weerlive-',
	},
	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

        -- Use some color variables
        local cGreen = '#008000;'
        local cYellow = '#ffff00;'
        local cOrange = '#ffa500;'
        local cRed = '#ff0000;'	
        local cWhite = '#FFFFFF;'
        local cBlue = '#0000FF;'
        local cGrey = '#8d8d8d;'

        local htmlColor = '#3b3bc4;'
        --Adjust lineheight
        local lineHeight = 1.1
        
        local weeralarm_value = dz.devices(wl_alarm_idx).sensorValue  -- Gets last stored value 
        local verw = ''     --Holds weather forcast
        local samenv = ''   --Holds weather summary
        local d1date = ''   --Holds date for today
        local d1nslperc = ''    --Holds neerslperc for today
        local d1znperc = ''     --Holds sun perc for today
        local d2date = ''       --Holds date for tomorrow
        local d2nslperc = ''    --Holds neerslperc for tomorrow
        local d2znperc = ''     --Holds sun perc for tomorrow
        local firstRain = 'norain'  -- Holds first rain hour.
        local lastRain = 'norain'   -- Holds first rain hour.
        local countRain = 0         -- Holds total expected rain in mm.
        local rainText = ''         -- Holds text to update rain forcast device.

        -- Local Functions go here =============
        local function gethumstat( lv )
            -- Calculate the humidity status.
            lv = tonumber( lv )
            if lv < 35 then return dz.HUM_DRY
                elseif lv >= 35 and lv < 45 then return dz.HUM_NORMAL
                elseif lv >= 45 and lv < 55 then return dz.HUM_COMFORTABLE
                elseif lv >= 55 and lv < 65 then return dz.HUM_NORMAL
                else return dz.HUM_WET
            end
        end
    
        local function getforecast( image ) 
            -- Define the BARO forcast.
            if image == "zonnig" or image == "helderenacht" then return dz.BARO_SUNNY
                elseif image == "halfbewolkt" or image == "mist" or image == "lichtbewolkt" or image == "nachtmist" then return dz.BARO_PARTLYCLOUDY
                elseif image == "bewolkt" or image == "zwaarbewolkt" or image == "wolkennacht" then return dz.BARO_CLOUDY
                elseif image == "regen" or image == 'halfbewolkt_regen' or image == "buien" or image == "hagel" or image == "sneeuw" then return dz.BARO_RAIN
                elseif image == "bliksem" then return dz.BARO_THUNDERSTORM
            else 
                return dz.BARO_NOINFO
            end
        end
            
        local function getIconNumber( image )
            if image == 'bewolkt' then iconNumber = 121 end
            if image == 'bliksem' then iconNumber = 122 end
            if image == 'hagel' then iconNumber = 123 end
            if image == 'halfbewolkt' then iconNumber = 124 end
            if image == 'helderenacht' then iconNumber = 125 end
            if image == 'lichtbewolkt' then iconNumber = 126 end
            if image == 'mist' then iconNumber = 127 end
            if image == 'nachtmist' then iconNumber = 128 end
            if image == 'regen' or image == 'buien' or image == 'halfbewolkt_regen' then iconNumber = 129 end
            if image == 'sneeuw' then iconNumber = 130 end
            if image == 'wolkennacht' or image == 'nachtbewolkt' then iconNumber = 131 end
            if image == 'zonnig' then iconNumber = 132 end
            if image == 'zwaarbewolkt' then iconNumber = 133 end
            -- For not existing image tekst called when no rain expected.
            if image == 'geenregen' then iconNumber = 134 end
            return iconNumber
        end

        local function getWindDirectionString( windDirection )
            -- To calculate the 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 = 'WNW' 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 ============
        -- Get the data.
		if (triggeredItem.isTimer) then
            -- Retrieve the data.
			dz.openURL({
				url = 'https://weerlive.nl/api/weerlive_api_v2.php?key=' .. wl_apikey .. '&locatie=' .. lat .. ',' .. long,
				method = 'GET',
				callback = 'weerlive'
			})
		end	

	    if (triggeredItem.isHTTPResponse) then
            -- Process the obtained data.
	        -- 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 )
                    
                    --- API ---------------------------------------
                    local api_table = result_table['api']
                    if type(api_table) == "table" then
                        dz.log( 'api_table: type = ' .. type(api_table), dz.LOG_DEBUG )
                        -- The data we get is:
                        -- "bron": "Bron: Weerdata KNMI/NOAA via Weerlive.nl",
                        -- "max_verz": 300,
                        -- "rest_verz": 90
                        local rest = api_table[1].rest_verz
                        dz.log( 'Remaining calls to weerlive.nl api for today = ' .. rest, dz.LOG_DEBUG )
                    else
                        dz.log( 'No api_table found', dz.LOG_ERROR )
                    end

                    --- LIVEWEER ---------------------------------
                    local liveweer_table = result_table['liveweer']
                    if type(liveweer_table) == "table" then
                        dz.log( 'liveweer_table: type = ' .. type(liveweer_table), dz.LOG_DEBUG )
                        -- The data we get is:
                        --"plaats": "Your City",
                        --"timestamp": 1714650484,
                        --"time": "02-05-2024 13:48:04",
                        --"temp": 23.2,
                        --"gtemp": 22.7,
                        --"samenv": "Licht bewolkt",
                        --"lv": 64,
                        --"windr": "O",
                        --"windrgr": 91.8,
                        --"windms": 2.14,
                        --"windbft": 2,
                        --"windknp": 4.2,
                        --"windkmh": 7.7,
                        --"luchtd": 998.851,
                        --"ldmmhg": 749,
                        --"dauwp": 16.1,
                        --"zicht": 27600,
                        --"gr": 796,
                        --"verw": "Vanaf vanmiddag onweersbuien. Vrijdag koeler weer met af en toe regen",
                        --"sup": "06:06",
                        --"sunder": "21:11",
                        --"image": "lichtbewolkt",
                        --"alarm": 0,
                        --"lkop": "Vanmiddag en vanavond plaatselijk overlast door onweersbuien",
                        --"ltekst": " Tot halverwege de middag zijn er geen waarschuwingen van kracht.Later vanmiddag en vanavond komen er vooral in het midden en zuiden onweersbuien voor. Deze buien kunnen plaatselijk gepaard gaan met windstoten, hagel en veel regen. Hierdoor kan er hinder zijn voor verkeer en buitenactiviteiten. Ook kan er plaatselijk wateroverlast zijn.Later in de avond neemt het aantal buien af en worden ze minder zwaar.Wat kan ik verwachten en wat kan ik doen?Meer detailsBij een onweersbui windstoten tot ca. 65 km/u. Hagel tot 2 cm. Daarnaast plaatselijk neerslaghoeveelheden van 20-40 mm in een uur.",
                        --"wrschklr": "groen",
                        --"wrsch_g": "02-05-2024 21:00",
                        --"wrsch_gts": 1714676400,
                        --"wrsch_gc": "geel"

                        -- TempHumBaro.
                        local temp = liveweer_table[1].temp -- Also used in with the wind device.
                        if wl_thb_idx ~= 99999 then 
                            local lv = liveweer_table[1].lv
                            local status = gethumstat( lv )
                            local luchtd = liveweer_table[1].luchtd
                            -- Just a quick side trip here for tomorrow's image
                            dz.log( 'Image text for tomorrow = '.. result_table.wk_verw[2].image, dz.LOG_DEBUG )
                            local image = result_table.wk_verw[2].image
                            local forecast = getforecast( image )
                            dz.log( 'Update TempHumBaro: Temp: ' .. temp .. ', Hum: ' .. lv .. ', Status: ' .. status .. ', Luchtdr: ' .. luchtd .. ', Forecast:' .. forecast, dz.LOG_DEBUG )
                            dz.devices(wl_thb_idx).updateTempHumBaro( temp, lv, status, luchtd, forecast )
                        end

                        -- Apparent temperature
                        local gtemp = liveweer_table[1].gtemp -- Also used in with the wind device.
                        if wl_gtemp_idx ~= 99999 then 
                            dz.log( 'Update gTemp: ' .. gtemp, dz.LOG_DEBUG )
                            dz.devices(wl_gtemp_idx).updateTemperature( gtemp )
                        end

                        -- Wind
                        if wl_wind_idx ~= 99999 then  
                            local windms = _u.round( liveweer_table[1].windms, 1 )
                            local windrgr = liveweer_table[1].windrgr
                            local windDirectionString = getWindDirectionString( windrgr )
                            --local windGust = result_table.current.wind_gusts_10m
                            dz.log( 'Update Windgr : ' .. windrgr .. ', Winddirection' .. windDirectionString .. ', Wind m/s: ' .. windms .. ', Gust: 0'  .. ', Temp: ' .. temp .. ', gTemp: ' .. gtemp, dz.LOG_DEBUG )
                            dz.devices(wl_wind_idx).updateWind( windrgr, windDirectionString, windms, 0, temp, gtemp )
                        end

                        -- Visibility
                        if wl_zicht_idx ~= 99999 then
                            local zicht = liveweer_table[1].zicht
                            dz.log( 'Update zicht: ' .. zicht / 1000, dz.LOG_DEBUG )
                            dz.devices(wl_zicht_idx).updateVisibility( zicht / 1000 )
                        end

                        -- Global radiation Watt/M2
                        if wl_gr_idx ~= 99999 then
                            local gr = liveweer_table[1].gr
                            dz.log( 'Update global radiation: ' .. gr, dz.LOG_DEBUG )
                            dz.devices(wl_gr_idx).updateRadiation( gr )                           
                        end
                        
                        -- Weather alert
                        if wl_alarm_idx ~= 99999 then
                            local alarm = liveweer_table[1].alarm
                            local lkop = liveweer_table[1].lkop
                            local ltekst = liveweer_table[1].ltekst
                            local wrschklr = liveweer_table[1].wrschklr
                            local wrsch_g = liveweer_table[1].wrsch_g
                            local wrsch_gts = liveweer_table[1].wrsch_gts
                            local wrsch_gc = liveweer_table[1].wrsch_gc

                            local alertText = lkop

                            --Set the alert color
                            if wrschklr == 'rood' then
                                alertLevel = dz.ALERTLEVEL_RED
                            elseif wrschklr == 'oranje' then
                                alertLevel = dz.ALERTLEVEL_ORANGE
                            elseif wrschklr == 'geel' then
                                alertLevel = dz.ALERTLEVEL_YELLOW
                            else
                                alertLevel = dz.ALERTLEVEL_GREY
                            end

                            if wrsch_g ~= '-'  then
                                -- There is a Warning
                                if wrsch_gc == 'rood' then
                                    htmlbgColor = cRed
                                elseif wrsch_gc == 'oranje' then
                                    htmlbgColor = cOrange
                                elseif wrsch_gc == 'geel' then
                                    htmlbgColor = cYellow
                                else
                                    htmlbgColor = cWhite
                                end
                                -- Add warning time and message on new line with the warning color.
                                alertText = alertText .. '\n<span style="line-height:' .. lineHeight .. ';  background-color: ' .. htmlbgColor .. '">Vanaf ' .. wrsch_g .. ' geldt code ' .. wrsch_gc .. '.</span>'                                
                            end
                        
                            -- Update the alert device with new alertLevel and alertText.
                            dz.log( 'Update Weeralarm: ' .. alertText, dz.LOG_DEBUG )
                            dz.devices(wl_alarm_idx).updateAlertSensor(alertLevel, alertText )
                        end

                        -- Summary and forecast used for wl_verw_idx
                        samenv = liveweer_table[1].samenv
                        verw =  liveweer_table[1].verw

                    else
                        dz.log( 'No liveweer_table found', dz.LOG_ERROR )
                    end

                    ---- WK_VERW ----------------------------------
                    local wk_verw_table = result_table['wk_verw']
                    if type(wk_verw_table) == "table" then
                        dz.log( 'wk_verw_table: type = ' .. type(wk_verw_table), dz.LOG_DEBUG )
                        -- The data we get is for 5 days, including today:
                        --"dag": "12-05-2024",
                        --"image": "halfbewolkt",
                        --"max_temp": 18,
                        --"min_temp": 17,
                        --"windbft": 3,
                        --"windkmh": 18,
                        --"windknp": 10,
                        --"windms": 5,
                        --"windrgr": 121,
                        --"windr": "ZO",
                        --"neersl_perc_dag": 50,
                        --"zond_perc_dag": 68

                        d1date = string.sub( wk_verw_table[1].dag, 1, 5 )   -- We use only 'dd-mm'
                        d1nslperc = wk_verw_table[1].neersl_perc_dag
                        d1znperc = wk_verw_table[1].zond_perc_dag
                        d2date = string.sub( wk_verw_table[2].dag, 1, 5 )   -- We use only 'dd-mm'
                        d2nslperc = wk_verw_table[2].neersl_perc_dag 
                        d2znperc = wk_verw_table[2].zond_perc_dag
                        
                        if wl_verw_idx ~= 99999 then
                            -- To calculate the expected sun hours.
                            local sunrise = dz.time.sunriseInMinutes
                            local sunset = dz.time.sunsetInMinutes
                            
                            -- For today -------------------------------------
                            local suntimeMinutes = ( d1znperc / 100 ) * ( sunset - sunrise ) 
                            local sunHours = suntimeMinutes / 60
                            local integerPart, fractionalPart = math.modf( sunHours )
                            fractionalPart = _u.round( fractionalPart * 60, 0 )
                            local sunMinutes = string.format( "%02d", fractionalPart )
                            local sunTime = integerPart .. ':' .. sunMinutes .. ' uur'
                            dz.log( 'Calculated sun time today = ' .. sunTime, dz.LOG_DEBUG )

                            local d1mintemp = wk_verw_table[1].min_temp
                            local d1maxtemp = wk_verw_table[1].max_temp

                            -- arrowup = &#9650; arrowdown = &#9660; Degree celsius = &#8451;
                            local texta = d1date .. ': <span style="color: ' ..  cGrey .. '">&#9660;</span>Temp. ' .. d1mintemp .. ' &#8451; - <span style="color: ' ..  cGrey .. '">&#9650;</span>Temp. ' .. d1maxtemp .. ' &#8451; - Zon ' .. sunTime
                            dz.log( texta, dz.LOG_DEBUG )

                            -- For tomorrow ----------------------------------
                            local suntimeMinutes = ( d2znperc / 100 ) * ( sunset - sunrise ) 
                            local sunHours = suntimeMinutes / 60
                            local integerPart, fractionalPart = math.modf( sunHours )
                            fractionalPart = _u.round( fractionalPart * 60, 0 )
                            local sunMinutes = string.format( "%02d", fractionalPart )
                            local sunTime = integerPart .. ':' .. sunMinutes .. ' uur'
                            dz.log( 'Calculated sun time tomorrow = ' .. sunTime, dz.LOG_DEBUG )

                            local d2mintemp = wk_verw_table[2].min_temp
                            local d2maxtemp = wk_verw_table[2].max_temp

                            -- arrowup = &#9650; arrowdown = &#9660; Degree celsius = &#8451;
                            local textb = d2date .. ': <span style="color: ' ..  cGrey .. '">&#9660;</span>Temp. ' .. d2mintemp .. ' &#8451; - <span style="color: ' ..  cGrey .. '">&#9650;</span>Temp. ' .. d2maxtemp .. ' &#8451; - Zon ' .. sunTime
                            dz.log( textb, dz.LOG_DEBUG )

                            -- Summary and forcast text.
                            dz.log( 'Samenvatting: ' .. samenv, dz.LOG_DEBUG ) 
                            dz.log( 'Verwachting: ' .. verw, dz.LOG_DEBUG )
                            local verw_text = '<span style="color: ' ..  htmlColor .. '">' .. samenv .. '\n' .. verw .. '</span>\n' .. texta .. '\n' .. textb

                            -- Update with new Text.
                            dz.log( 'Update Verwachting: ' .. verw_text, dz.LOG_DEBUG )
                            dz.devices(wl_verw_idx).updateText( verw_text )

                            if wl_icons_installed == 1 then
                                -- Update icon for tomorrow forecast.
                                local image = wk_verw_table[2].image
                                local iconNumber = getIconNumber( image )
                                dz.devices(wl_verw_idx).setIcon(iconNumber)
                            end
                        end                        
                    else
                        dz.log( 'No wk_verw_table found', dz.LOG_ERROR )
                    end

                    ---- UUR_VERW ----------------------------------
                    local uur_verw_table = result_table['uur_verw']
                    if type(uur_verw_table) == "table" then
                        dz.log( 'uur_verw_table: type = ' .. type(uur_verw_table), dz.LOG_DEBUG )
                        -- The data we get is for 24 hr, starting from next whole hour:
                        --"uur": "13-05-2024 21:00",
                        --"timestamp": 1715626800,
                        --"image": "wolkennacht",
                        --"temp": 20,
                        --"windbft": 2,
                        --"windkmh": 7,
                        --"windknp": 4,
                        --"windms": 2,
                        --"windrgr": 130,
                        --"windr": "ZO",
                        --"neersl": 0,
                        --"gr": 6
                       
                        if wl_rain_idx ~= 99999 then 
                            -- Loop through the table. Search for the first rain occurence.
                            local tc = #uur_verw_table
                            for i = 1, tc do                    
                                -- if found: Rain expexted at 'uur'
                                -- else: No rain expected until (last time found.)
                                local neersl = uur_verw_table[i].neersl
                                local hr = uur_verw_table[i].uur
                                if neersl ~= 0 then
                                    countRain = countRain + neersl
                                    local unixTime = dz.time.dateToTimestamp( hr, 'dd-mm-yyyy hh:MM' )
                                    if firstRain == 'norain' then firstRain = dz.time.timestampToDate( unixTime, 'hh:MM' , 0 ) end
                                    lastRain = dz.time.timestampToDate( unixTime, 'hh:MM' , 3600 ) -- is the next hour. 
                                else
                                    if firstRain ~= 'norain' then
                                        -- neersl == 0 after first episode of rain, so exit the loop.
                                        break
                                    end
                                end
                            end
                            if firstRain == 'norain' then
                                rainText = 'Geen neerslag verwacht < 24 uur'
                                dz.log( rainText, dz.LOG_DEBUG )
                            else
                                rainText = '<span style="color: ' ..  htmlColor .. '">1<sup>e</sup> Neerslag (totaal ' .. countRain .. ' l/m<sup>2</sup>) tussen ' .. firstRain .. ' - ' .. lastRain .. ' uur</span>'
                                dz.log( rainText, dz.LOG_DEBUG )
                            end
                            local rainTexta = d1date .. ': Kans op neerslag ' .. d1nslperc .. '%.'
                            dz.log( rainTexta, dz.LOG_DEBUG )
                            local rainTextb = d2date .. ': Kans op neerslag ' .. d2nslperc .. '%.'
                            dz.log( rainTextb, dz.LOG_DEBUG )

                             -- Update with new Text.
                            dz.log( 'Update Regenverwachting: ' .. rainText .. '\n' .. rainTexta .. '\n' .. rainTextb, dz.LOG_DEBUG )
                            dz.devices(wl_rain_idx).updateText( rainText .. '\n' .. rainTexta .. '\n' .. rainTextb )

                            if wl_icons_installed == 1 then
                                if rainText == 'Geen neerslag verwacht < 24 uur' then
                                    local iconNumber = getIconNumber( 'geenregen')
                                    dz.devices(wl_rain_idx).setIcon(iconNumber)
                                else
                                    -- Get rain icon
                                    local iconNumber = getIconNumber( 'regen' ) 
                                    dz.devices(wl_rain_idx).setIcon(iconNumber)
                                end
                            end
                        end
                    else
                        dz.log( 'No uur_verw_table found', dz.LOG_ERROR )
                    end
                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
}
-- That's All --------------------------------------------------
Last edited by janpep on Friday 17 May 2024 9:15, edited 1 time in total.
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.
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

Hereby the icons I used. I used free icons from https://www.flaticon.com/icons and modified a few.

Edit: Replaced ZIP file with new icons 48x48 transparent and 16x16 flooplan icon.
Attachments
Weerlive-DZ-ScriptIcons.zip
(60.1 KiB) Downloaded 41 times
Last edited by janpep on Tuesday 08 October 2024 15:01, edited 3 times in total.
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.
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

I corrected the URL to see all the devices in my first post.
It is:

Code: Select all

https://yourIP:yourport/json.htm?type=command&param=getdevices&plan=0
I use it so often that I included it in a custom page, so that I can easily access it from the menu. I had pasted the URL of that page into the message without a second thought. My apologies for the confusion.
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.
Jan Jansen
Posts: 229
Joined: Wednesday 30 April 2014 20:27
Target OS: Raspberry Pi / ODroid
Domoticz version: Stable
Location: The Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by Jan Jansen »

Nice presentation of data, nice that the weather alarm is integrated. Separate temperature devices for the maximum and minimum temperatures of today and tomorrow would be a nice addition.
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

I have considered that, but (just like with the Buienradar) it requires a lot of separate devices, while it doesn't give me any advantage.
Since I don't need to see a trend (graph) in expectations(!) and I also do not need these Min and Max temperatures as a source for use elsewhere, I wanted to keep it as compact as possible.
I have already 20 temperature devices for thermostat, radiator knobs, air conditioning, hard drives, smoke detectors etc. :lol:
I briefly considered adding a notification in the event of expected night frost (risk of slippery conditions), but my car already indicates that.
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.
User avatar
waltervl
Posts: 5847
Joined: Monday 28 January 2019 18:48
Target OS: Linux
Domoticz version: 2024.7
Location: NL
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by waltervl »

Jan Jansen wrote: Friday 17 May 2024 14:44 Nice presentation of data, nice that the weather alarm is integrated. Separate temperature devices for the maximum and minimum temperatures of today and tomorrow would be a nice addition.
You could look at the Openweathermap Integration. Lost of devices that are created.
https://www.domoticz.com/wiki/Open_Weather_Map
Domoticz running on Udoo X86 (on Ubuntu)
Devices/plugins: ZigbeeforDomoticz (with Xiaomi, Ikea, Tuya devices), Nefit Easy, Midea Airco, Omnik Solar, Goodwe Solar
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

Jan Jansen wrote: Friday 17 May 2024 14:44 Separate temperature devices for the maximum and minimum temperatures of today and tomorrow would be a nice addition.
If you want to use it, I am willing to look into adding them optionally. That is in line with how it is set up and what i ment with 'configurable'.
Then, if you want to use them, you add their idx numbers and if not you keep it at the dummy idx 99999 and it will be skipped.

But it might get a bit complicated, because then you probably do not want to see it also in the forcast text device. I want to be a little reserved with things that I do not need/want/like for my own use.
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.
HvdW
Posts: 612
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by HvdW »

janpep wrote: Friday 17 May 2024 15:37 I briefly considered adding a notification in the event of expected night frost (risk of slippery conditions), but my car already indicates that.
You wouldn't know how it is to live without that.
All our Agapanthuses and other non frost resistant plants would die in one night of neglect.
I added it with a notification.
Bugs bug me.
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

HvdW wrote: Friday 17 May 2024 17:11 All our Agapanthuses and other non frost resistant plants would die in one night of neglect.
I added it with a notification.
Do not buy such plants :-)
When there is a MIN device, you could let that trigger a nofication. I will look into it.
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.
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

OK. In order not to pollute my production environment, I adjusted the script in my test environment. I quickly added the 4 temperature devices and tested the updated script.
When you use them fill in the idx for the min and max temperatures for today and tomorrrow.
You can connect your notifications to the devices as you like. Or you can just keep houseplants indoors. :-)
When you use them, these temperatures will disapear from the forcast device as shown in the picture below.
When you do not use them, it remains as it was with the min/max temps.
Weerlive-4-JanPep.png
Weerlive-4-JanPep.png (37.79 KiB) Viewed 3902 times
Weerlive-5-JanPep.png
Weerlive-5-JanPep.png (59.15 KiB) Viewed 3902 times
Updated script:

Code: Select all

-- 12-05-2024 script by Jan Peppink, https://ict.peppink.nl
-- Started for weather alarm Expanded as renewed custom script instead of Buienradar.
-- Reduce the number of weather information sources.
--
-- Extra explaining comments and choices made ------------------------------------
-- Explanation of the api can be found at https://weerlive.nl/delen.php, where you also can obtain an api key.
-- Optional use of devices. You can skip them by giving them a dummy idx of 99999.
-- The BARO device forcast uses the 'image' text that is found for tomorrow.
-- WindGust is not yet part of weerlive api result. Hardcoded set to 0
-- Optional use of custom icons. Activate by setting wl_icons_installed = 1
--   NB. DOUBLE check function getIconNumber and correct your iconNumbers !!
-- The Weatherforcast device gets the icon that comes with forcast tomorrow.
--   May sometimes be a bit confusing with the text that is shown.
-- The Rainforcast device gets the icon rain or NO rain, when not expected < 24 hours.
-- The script translates mm rainfall to l/m2 which is easier to understand.
-- The script calculates expected sun hours given as zond_perc_dag of the time between sunrise and sunset to hh:MM.
-- An explanation about this can be found at https://www.knmi.nl/kennis-en-datacentrum/achtergrond/de-weersverwachting
-- 17-05-2024 Added 4 optional temperature devices for Min and Max Temp.

--- Your settings ----------------------------------------------------------------
-- First set the used device index numbers and variables you might want to change.
---#################################################################
-- NB! To configure, set the devices you do NOT want to use to 99999
-- Easy to temporarily or permanently not use a device without rearanging the code.
---#################################################################
local wl_alarm_idx = 99999    --idx of the custom Alert device for Weather alarm.
local wl_thb_idx = 99999    --idx of the Temperature+humidity+barometer device adapter.
local wl_gtemp_idx = 99999    --idx of the Temperature device adapter.
local wl_wind_idx = 99999    --idx of the Wind device adapter.
local wl_zicht_idx = 99999    --idx of the Visibility device.
local wl_gr_idx = 99999    --idx of the Global Solar radiation device adapter.
local wl_verw_idx = 99999    --idx of the weatherforcast Text device.
local wl_rain_idx = 99999    --idx of the Rain forcast Text device.
local wl_mintoD_idx = 99999    --idx of minTemp toDay Temperature device adapter.
local wl_maxtoD_idx = 99999    --idx of maxTemp toDay Temperature device adapter.
local wl_mintoM_idx = 99999    --idx of minTemp toMorrow Temperature device adapter.
local wl_maxtoM_idx = 99999    --idx of maxTemp toMorrow Temperature device adapter.

local wl_apikey = 'yourApiKey' -- Access for weerlive.nl with your apikey.
local wl_icons_installed = 0    -- 1 or 0 when custom icons have been installed or not.

----------------------------------------------------------------------------------
return {
	on = {
		timer = { 
            --'every 2 minutes',  -- Only for testing.
            'every 10 minutes', 
        },
        httpResponses = {
		    'weerlive'       -- 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 = domoticz.LOG_INFO,
        --level = domoticz.LOG_DEBUG,
		marker = 'Weerlive-',
	},
	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

        -- Use some color variables
        local cGreen = '#008000;'
        local cYellow = '#ffff00;'
        local cOrange = '#ffa500;'
        local cRed = '#ff0000;'	
        local cWhite = '#FFFFFF;'
        local cBlue = '#0000FF;'
        local cGrey = '#8d8d8d;'

        local htmlColor = '#3b3bc4;'
        --Adjust lineheight
        local lineHeight = 1.1
        
        local weeralarm_value = dz.devices(wl_alarm_idx).sensorValue  -- Gets last stored value 
        local verw = ''     --Holds weather forcast
        local samenv = ''   --Holds weather summary
        local d1date = ''   --Holds date for today
        local d1nslperc = ''    --Holds neerslperc for today
        local d1znperc = ''     --Holds sun perc for today
        local d2date = ''       --Holds date for tomorrow
        local d2nslperc = ''    --Holds neerslperc for tomorrow
        local d2znperc = ''     --Holds sun perc for tomorrow
        local firstRain = 'norain'  -- Holds first rain hour.
        local lastRain = 'norain'   -- Holds first rain hour.
        local countRain = 0         -- Holds total expected rain in mm.
        local rainText = ''         -- Holds text to update rain forcast device.

        -- Local Functions go here =============
        local function gethumstat( lv )
            -- Calculate the humidity status.
            lv = tonumber( lv )
            if lv < 35 then return dz.HUM_DRY
                elseif lv >= 35 and lv < 45 then return dz.HUM_NORMAL
                elseif lv >= 45 and lv < 55 then return dz.HUM_COMFORTABLE
                elseif lv >= 55 and lv < 65 then return dz.HUM_NORMAL
                else return dz.HUM_WET
            end
        end
    
        local function getforecast( image ) 
            -- Define the BARO forcast.
            if image == "zonnig" or image == "helderenacht" then return dz.BARO_SUNNY
                elseif image == "halfbewolkt" or image == "mist" or image == "lichtbewolkt" or image == "nachtmist" then return dz.BARO_PARTLYCLOUDY
                elseif image == "bewolkt" or image == "zwaarbewolkt" or image == "wolkennacht" then return dz.BARO_CLOUDY
                elseif image == "regen" or image == 'halfbewolkt_regen' or image == "buien" or image == "hagel" or image == "sneeuw" then return dz.BARO_RAIN
                elseif image == "bliksem" then return dz.BARO_THUNDERSTORM
            else 
                return dz.BARO_NOINFO
            end
        end
            
        local function getIconNumber( image )
            if image == 'bewolkt' then iconNumber = 121 end
            if image == 'bliksem' then iconNumber = 122 end
            if image == 'hagel' then iconNumber = 123 end
            if image == 'halfbewolkt' then iconNumber = 124 end
            if image == 'helderenacht' then iconNumber = 125 end
            if image == 'lichtbewolkt' then iconNumber = 126 end
            if image == 'mist' then iconNumber = 127 end
            if image == 'nachtmist' then iconNumber = 128 end
            if image == 'regen' or image == 'buien' or image == 'halfbewolkt_regen' then iconNumber = 129 end
            if image == 'sneeuw' then iconNumber = 130 end
            if image == 'wolkennacht' or image == 'nachtbewolkt' then iconNumber = 131 end
            if image == 'zonnig' then iconNumber = 132 end
            if image == 'zwaarbewolkt' then iconNumber = 133 end
            -- For not existing image tekst called when no rain expected.
            if image == 'geenregen' then iconNumber = 134 end
            return iconNumber
        end

        local function getWindDirectionString( windDirection )
            -- To calculate the 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 = 'WNW' 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 ============
        -- Get the data.
		if (triggeredItem.isTimer) then
            -- Retrieve the data.
			dz.openURL({
				url = 'https://weerlive.nl/api/weerlive_api_v2.php?key=' .. wl_apikey .. '&locatie=' .. lat .. ',' .. long,
				method = 'GET',
				callback = 'weerlive'
			})
		end	

	    if (triggeredItem.isHTTPResponse) then
            -- Process the obtained data.
	        -- 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 )
                    
                    --- API ---------------------------------------
                    local api_table = result_table['api']
                    if type(api_table) == "table" then
                        dz.log( 'api_table: type = ' .. type(api_table), dz.LOG_DEBUG )
                        -- The data we get is:
                        -- "bron": "Bron: Weerdata KNMI/NOAA via Weerlive.nl",
                        -- "max_verz": 300,
                        -- "rest_verz": 90
                        local rest = api_table[1].rest_verz
                        dz.log( 'Remaining calls to weerlive.nl api for today = ' .. rest, dz.LOG_DEBUG )
                    else
                        dz.log( 'No api_table found', dz.LOG_ERROR )
                    end

                    --- LIVEWEER ---------------------------------
                    local liveweer_table = result_table['liveweer']
                    if type(liveweer_table) == "table" then
                        dz.log( 'liveweer_table: type = ' .. type(liveweer_table), dz.LOG_DEBUG )
                        -- The data we get is:
                        --"plaats": "Your City",
                        --"timestamp": 1714650484,
                        --"time": "02-05-2024 13:48:04",
                        --"temp": 23.2,
                        --"gtemp": 22.7,
                        --"samenv": "Licht bewolkt",
                        --"lv": 64,
                        --"windr": "O",
                        --"windrgr": 91.8,
                        --"windms": 2.14,
                        --"windbft": 2,
                        --"windknp": 4.2,
                        --"windkmh": 7.7,
                        --"luchtd": 998.851,
                        --"ldmmhg": 749,
                        --"dauwp": 16.1,
                        --"zicht": 27600,
                        --"gr": 796,
                        --"verw": "Vanaf vanmiddag onweersbuien. Vrijdag koeler weer met af en toe regen",
                        --"sup": "06:06",
                        --"sunder": "21:11",
                        --"image": "lichtbewolkt",
                        --"alarm": 0,
                        --"lkop": "Vanmiddag en vanavond plaatselijk overlast door onweersbuien",
                        --"ltekst": " Tot halverwege de middag zijn er geen waarschuwingen van kracht.Later vanmiddag en vanavond komen er vooral in het midden en zuiden onweersbuien voor. Deze buien kunnen plaatselijk gepaard gaan met windstoten, hagel en veel regen. Hierdoor kan er hinder zijn voor verkeer en buitenactiviteiten. Ook kan er plaatselijk wateroverlast zijn.Later in de avond neemt het aantal buien af en worden ze minder zwaar.Wat kan ik verwachten en wat kan ik doen?Meer detailsBij een onweersbui windstoten tot ca. 65 km/u. Hagel tot 2 cm. Daarnaast plaatselijk neerslaghoeveelheden van 20-40 mm in een uur.",
                        --"wrschklr": "groen",
                        --"wrsch_g": "02-05-2024 21:00",
                        --"wrsch_gts": 1714676400,
                        --"wrsch_gc": "geel"

                        -- TempHumBaro.
                        local temp = liveweer_table[1].temp -- Also used in with the wind device.
                        if wl_thb_idx ~= 99999 then 
                            local lv = liveweer_table[1].lv
                            local status = gethumstat( lv )
                            local luchtd = liveweer_table[1].luchtd
                            -- Just a quick side trip here for tomorrow's image
                            dz.log( 'Image text for tomorrow = '.. result_table.wk_verw[2].image, dz.LOG_DEBUG )
                            local image = result_table.wk_verw[2].image
                            local forecast = getforecast( image )
                            dz.log( 'Update TempHumBaro: Temp: ' .. temp .. ', Hum: ' .. lv .. ', Status: ' .. status .. ', Luchtdr: ' .. luchtd .. ', Forecast:' .. forecast, dz.LOG_DEBUG )
                            dz.devices(wl_thb_idx).updateTempHumBaro( temp, lv, status, luchtd, forecast )
                        end

                        -- Apparent temperature
                        local gtemp = liveweer_table[1].gtemp -- Also used in with the wind device.
                        if wl_gtemp_idx ~= 99999 then 
                            dz.log( 'Update gTemp: ' .. gtemp, dz.LOG_DEBUG )
                            dz.devices(wl_gtemp_idx).updateTemperature( gtemp )
                        end

                        -- Wind
                        if wl_wind_idx ~= 99999 then  
                            local windms = _u.round( liveweer_table[1].windms, 1 )
                            local windrgr = liveweer_table[1].windrgr
                            local windDirectionString = getWindDirectionString( windrgr )
                            --local windGust = result_table.current.wind_gusts_10m
                            dz.log( 'Update Windgr : ' .. windrgr .. ', Winddirection' .. windDirectionString .. ', Wind m/s: ' .. windms .. ', Gust: 0'  .. ', Temp: ' .. temp .. ', gTemp: ' .. gtemp, dz.LOG_DEBUG )
                            dz.devices(wl_wind_idx).updateWind( windrgr, windDirectionString, windms, 0, temp, gtemp )
                        end

                        -- Visibility
                        if wl_zicht_idx ~= 99999 then
                            local zicht = liveweer_table[1].zicht
                            dz.log( 'Update zicht: ' .. zicht / 1000, dz.LOG_DEBUG )
                            dz.devices(wl_zicht_idx).updateVisibility( zicht / 1000 )
                        end

                        -- Global radiation Watt/M2
                        if wl_gr_idx ~= 99999 then
                            local gr = liveweer_table[1].gr
                            dz.log( 'Update global radiation: ' .. gr, dz.LOG_DEBUG )
                            dz.devices(wl_gr_idx).updateRadiation( gr )                           
                        end
                        
                        -- Weather alert
                        if wl_alarm_idx ~= 99999 then
                            local alarm = liveweer_table[1].alarm
                            local lkop = liveweer_table[1].lkop
                            local ltekst = liveweer_table[1].ltekst
                            local wrschklr = liveweer_table[1].wrschklr
                            local wrsch_g = liveweer_table[1].wrsch_g
                            local wrsch_gts = liveweer_table[1].wrsch_gts
                            local wrsch_gc = liveweer_table[1].wrsch_gc

                            local alertText = lkop

                            --Set the alert color
                            if wrschklr == 'rood' then
                                alertLevel = dz.ALERTLEVEL_RED
                            elseif wrschklr == 'oranje' then
                                alertLevel = dz.ALERTLEVEL_ORANGE
                            elseif wrschklr == 'geel' then
                                alertLevel = dz.ALERTLEVEL_YELLOW
                            else
                                alertLevel = dz.ALERTLEVEL_GREY
                            end

                            if wrsch_g ~= '-'  then
                                -- There is a Warning
                                if wrsch_gc == 'rood' then
                                    htmlbgColor = cRed
                                elseif wrsch_gc == 'oranje' then
                                    htmlbgColor = cOrange
                                elseif wrsch_gc == 'geel' then
                                    htmlbgColor = cYellow
                                else
                                    htmlbgColor = cWhite
                                end
                                -- Add warning time and message on new line with the warning color.
                                alertText = alertText .. '\n<span style="line-height:' .. lineHeight .. ';  background-color: ' .. htmlbgColor .. '">Vanaf ' .. wrsch_g .. ' geldt code ' .. wrsch_gc .. '.</span>'                                
                            end
                        
                            -- Update the alert device with new alertLevel and alertText.
                            dz.log( 'Update Weeralarm: ' .. alertText, dz.LOG_DEBUG )
                            dz.devices(wl_alarm_idx).updateAlertSensor(alertLevel, alertText )
                        end

                        -- Summary and forecast used for wl_verw_idx
                        samenv = liveweer_table[1].samenv
                        verw =  liveweer_table[1].verw

                    else
                        dz.log( 'No liveweer_table found', dz.LOG_ERROR )
                    end

                    ---- WK_VERW ----------------------------------
                    local wk_verw_table = result_table['wk_verw']
                    if type(wk_verw_table) == "table" then
                        dz.log( 'wk_verw_table: type = ' .. type(wk_verw_table), dz.LOG_DEBUG )
                        -- The data we get is for 5 days, including today:
                        --"dag": "12-05-2024",
                        --"image": "halfbewolkt",
                        --"max_temp": 18,
                        --"min_temp": 17,
                        --"windbft": 3,
                        --"windkmh": 18,
                        --"windknp": 10,
                        --"windms": 5,
                        --"windrgr": 121,
                        --"windr": "ZO",
                        --"neersl_perc_dag": 50,
                        --"zond_perc_dag": 68

                        d1date = string.sub( wk_verw_table[1].dag, 1, 5 )   -- We use only 'dd-mm'
                        d1nslperc = wk_verw_table[1].neersl_perc_dag
                        d1znperc = wk_verw_table[1].zond_perc_dag
                        d1mintemp = wk_verw_table[1].min_temp
                        d1maxtemp = wk_verw_table[1].max_temp
                            
                        d2date = string.sub( wk_verw_table[2].dag, 1, 5 )   -- We use only 'dd-mm'
                        d2nslperc = wk_verw_table[2].neersl_perc_dag 
                        d2znperc = wk_verw_table[2].zond_perc_dag
                        d2mintemp = wk_verw_table[2].min_temp
                        d2maxtemp = wk_verw_table[2].max_temp

                        
                        if wl_mintoD_idx ~= 99999 then
                            dz.log( 'Update Min. Temp toDay: ' .. d1mintemp, dz.LOG_DEBUG )
                            dz.devices(wl_mintoD_idx).updateTemperature( d1mintemp )
                        end
                        if wl_maxtoD_idx ~= 99999 then
                            dz.log( 'Update Max. Temp toDay: ' .. d1maxtemp, dz.LOG_DEBUG )
                            dz.devices(wl_maxtoD_idx).updateTemperature( d1maxtemp )
                        end
                        if wl_mintoM_idx ~= 99999 then
                            dz.log( 'Update Min. Temp toMorrow: ' .. d2mintemp, dz.LOG_DEBUG )
                            dz.devices(wl_mintoM_idx).updateTemperature( d2mintemp )
                        end
                        if wl_maxtoM_idx ~= 99999 then
                            dz.log( 'Update Max. Temp toMorrow: ' .. d2maxtemp, dz.LOG_DEBUG )
                            dz.devices(wl_maxtoM_idx).updateTemperature( d2maxtemp )
                        end

                        if wl_verw_idx ~= 99999 then
                            -- To calculate the expected sun hours.
                            local sunrise = dz.time.sunriseInMinutes
                            local sunset = dz.time.sunsetInMinutes
                            
                            -- For today -------------------------------------
                            local suntimeMinutes = ( d1znperc / 100 ) * ( sunset - sunrise ) 
                            local sunHours = suntimeMinutes / 60
                            local integerPart, fractionalPart = math.modf( sunHours )
                            fractionalPart = _u.round( fractionalPart * 60, 0 )
                            local sunMinutes = string.format( "%02d", fractionalPart )
                            local sunTime = integerPart .. ':' .. sunMinutes .. ' uur'
                            dz.log( 'Calculated sun time today = ' .. sunTime, dz.LOG_DEBUG )

                            local texta = ''
                            if wl_mintoD_idx ~= 99999 and wl_maxtoD_idx ~= 99999 then
                                texta = d1date .. ': Verwachte uren zon ' .. sunTime
                            else
                                -- arrowup = &#9650; arrowdown = &#9660; Degree celsius = &#8451;
                                texta = d1date .. ': <span style="color: ' ..  cGrey .. '">&#9660;</span>Temp. ' .. d1mintemp .. ' &#8451; - <span style="color: ' ..  cGrey .. '">&#9650;</span>Temp. ' .. d1maxtemp .. ' &#8451; - Zon ' .. sunTime
                            end
                            dz.log( texta, dz.LOG_DEBUG )

                            -- For tomorrow ----------------------------------
                            local suntimeMinutes = ( d2znperc / 100 ) * ( sunset - sunrise ) 
                            local sunHours = suntimeMinutes / 60
                            local integerPart, fractionalPart = math.modf( sunHours )
                            fractionalPart = _u.round( fractionalPart * 60, 0 )
                            local sunMinutes = string.format( "%02d", fractionalPart )
                            local sunTime = integerPart .. ':' .. sunMinutes .. ' uur'
                            dz.log( 'Calculated sun time tomorrow = ' .. sunTime, dz.LOG_DEBUG )

                            local textb = ''
                            if wl_mintoM_idx ~= 99999 and wl_maxtoM_idx ~= 99999 then
                                textb = d2date .. ': Verwachte uren zon ' .. sunTime
                            else
                                -- arrowup = &#9650; arrowdown = &#9660; Degree celsius = &#8451;
                                textb = d2date .. ': <span style="color: ' ..  cGrey .. '">&#9660;</span>Temp. ' .. d2mintemp .. ' &#8451; - <span style="color: ' ..  cGrey .. '">&#9650;</span>Temp. ' .. d2maxtemp .. ' &#8451; - Zon ' .. sunTime
                            end
                            dz.log( textb, dz.LOG_DEBUG )

                            -- Summary and forcast text.
                            dz.log( 'Samenvatting: ' .. samenv, dz.LOG_DEBUG ) 
                            dz.log( 'Verwachting: ' .. verw, dz.LOG_DEBUG )
                            local verw_text = '<span style="color: ' ..  htmlColor .. '">' .. samenv .. '\n' .. verw .. '</span>\n' .. texta .. '\n' .. textb

                            -- Update with new Text.
                            dz.log( 'Update Verwachting: ' .. verw_text, dz.LOG_DEBUG )
                            dz.devices(wl_verw_idx).updateText( verw_text )

                            if wl_icons_installed == 1 then
                                -- Update icon for tomorrow forecast.
                                local image = wk_verw_table[2].image
                                local iconNumber = getIconNumber( image )
                                dz.devices(wl_verw_idx).setIcon(iconNumber)
                            end
                        end                        
                    else
                        dz.log( 'No wk_verw_table found', dz.LOG_ERROR )
                    end

                    ---- UUR_VERW ----------------------------------
                    local uur_verw_table = result_table['uur_verw']
                    if type(uur_verw_table) == "table" then
                        dz.log( 'uur_verw_table: type = ' .. type(uur_verw_table), dz.LOG_DEBUG )
                        -- The data we get is for 24 hr, starting from next whole hour:
                        --"uur": "13-05-2024 21:00",
                        --"timestamp": 1715626800,
                        --"image": "wolkennacht",
                        --"temp": 20,
                        --"windbft": 2,
                        --"windkmh": 7,
                        --"windknp": 4,
                        --"windms": 2,
                        --"windrgr": 130,
                        --"windr": "ZO",
                        --"neersl": 0,
                        --"gr": 6
                       
                        if wl_rain_idx ~= 99999 then 
                            -- Loop through the table. Search for the first rain occurence.
                            local tc = #uur_verw_table
                            for i = 1, tc do                    
                                -- if found: Rain expexted at 'uur'
                                -- else: No rain expected until (last time found.)
                                local neersl = uur_verw_table[i].neersl
                                local hr = uur_verw_table[i].uur
                                if neersl ~= 0 then
                                    countRain = countRain + neersl
                                    local unixTime = dz.time.dateToTimestamp( hr, 'dd-mm-yyyy hh:MM' )
                                    if firstRain == 'norain' then firstRain = dz.time.timestampToDate( unixTime, 'hh:MM' , 0 ) end
                                    lastRain = dz.time.timestampToDate( unixTime, 'hh:MM' , 3600 ) -- is the next hour. 
                                else
                                    if firstRain ~= 'norain' then
                                        -- neersl == 0 after first episode of rain, so exit the loop.
                                        break
                                    end
                                end
                            end
                            if firstRain == 'norain' then
                                rainText = 'Geen neerslag verwacht < 24 uur'
                                dz.log( rainText, dz.LOG_DEBUG )
                            else
                                rainText = '<span style="color: ' ..  htmlColor .. '">1<sup>e</sup> Neerslag (totaal ' .. countRain .. ' l/m<sup>2</sup>) tussen ' .. firstRain .. ' - ' .. lastRain .. ' uur</span>'
                                dz.log( rainText, dz.LOG_DEBUG )
                            end
                            local rainTexta = d1date .. ': Kans op neerslag ' .. d1nslperc .. '%.'
                            dz.log( rainTexta, dz.LOG_DEBUG )
                            local rainTextb = d2date .. ': Kans op neerslag ' .. d2nslperc .. '%.'
                            dz.log( rainTextb, dz.LOG_DEBUG )

                             -- Update with new Text.
                            dz.log( 'Update Regenverwachting: ' .. rainText .. '\n' .. rainTexta .. '\n' .. rainTextb, dz.LOG_DEBUG )
                            dz.devices(wl_rain_idx).updateText( rainText .. '\n' .. rainTexta .. '\n' .. rainTextb )

                            if wl_icons_installed == 1 then
                                if rainText == 'Geen neerslag verwacht < 24 uur' then
                                    local iconNumber = getIconNumber( 'geenregen')
                                    dz.devices(wl_rain_idx).setIcon(iconNumber)
                                else
                                    -- Get rain icon
                                    local iconNumber = getIconNumber( 'regen' ) 
                                    dz.devices(wl_rain_idx).setIcon(iconNumber)
                                end
                            end
                        end
                    else
                        dz.log( 'No uur_verw_table found', dz.LOG_ERROR )
                    end
                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
}
-- 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.
User avatar
HansieNL
Posts: 964
Joined: Monday 28 September 2015 15:13
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by HansieNL »

@janpep You did use the code:

Code: Select all

                            --Set the alert color
                            if wrschklr == 'rood' then
                                alertLevel = dz.ALERTLEVEL_RED
                            elseif wrschklr == 'oranje' then
                                alertLevel = dz.ALERTLEVEL_ORANGE
                            elseif wrschklr == 'geel' then
                                alertLevel = dz.ALERTLEVEL_YELLOW
                            else
                                alertLevel = dz.ALERTLEVEL_GREY
                            end
Is there a reason you didn't use the green alertlevel?
Blah blah blah
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

HansieNL wrote: Friday 17 May 2024 23:17 Is there a reason you didn't use the green alertlevel?
My thought here is that if there is NO alert, there is no reason ask the attention with an alert color.
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.
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

In addition, I sometimes find warnings a bit exaggerated. An ALERT code for a rain shower. It's not a tornado!
Then for me it's as if green means I now have permission to go outside :-). I like to decide that myself and therefore keep it more neutral with gray.
But you found the place to set it green as you like.
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.
User avatar
HansieNL
Posts: 964
Joined: Monday 28 September 2015 15:13
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by HansieNL »

Thanks @janpep for the explanation.
Blah blah blah
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

Here an example of the announcement of a 'code yellow'.
For now moment alarm == 0, but alert code is announced for 14:00 hr.
So, at 14:00 hour, when the alarm = 1, the alert will be set.
Weerlive-6-JanPep.png
Weerlive-6-JanPep.png (16.1 KiB) Viewed 3824 times
It's a choice. I had also considered setting the alert the first time the announcement comes but then there is no clear signal that it does not yet apply at this moment. That's why I kept the alarm status ( 0 or 1) leading.
As a result I have chosen to give it a colorcode background to draw the attention.
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.
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

And then when that time has come:
Weerlive-7-JanPep.png
Weerlive-7-JanPep.png (14.41 KiB) Viewed 3798 times
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.
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

I received a tip and also added the extended text of the weather forecast as an optional text device.
With a manipulation of the text because there is often a space missing after a sentence and for better readability I also added a little bit of extra line height.
It is a block of text that does not read very nice, but you have it.
Weerlive-8-JanPep.png
Weerlive-8-JanPep.png (30.41 KiB) Viewed 3732 times
Here the updated script.

Code: Select all

-- 12-05-2024 script by Jan Peppink, https://ict.peppink.nl
-- Started for weather alarm Expanded as renewed custom script instead of Buienradar.
-- Reduce the number of weather information sources.
--
-- Extra explaining comments and choices made ------------------------------------
-- Explanation of the api can be found at https://weerlive.nl/delen.php, where you also can obtain an api key.
-- Optional use of devices. You can skip them by giving them a dummy idx of 99999.
-- The BARO device forcast uses the 'image' text that is found for tomorrow.
-- WindGust is not yet part of weerlive api result. Hardcoded set to 0
-- Optional use of custom icons. Activate by setting wl_icons_installed = 1
--   NB. DOUBLE check function getIconNumber and correct your iconNumbers !!
-- The Weatherforcast device gets the icon that comes with forcast tomorrow.
--   May sometimes be a bit confusing with the text that is shown.
-- The Rainforcast device gets the icon rain or NO rain, when not expected < 24 hours.
-- The script translates mm rainfall to l/m2 which is easier to understand.
-- The script calculates expected sun hours given as zond_perc_dag of the time between sunrise and sunset to hh:MM.
-- An explanation about this can be found at https://www.knmi.nl/kennis-en-datacentrum/achtergrond/de-weersverwachting
-- 17-05-2024 Added 4 optional temperature devices for Min and Max Temp.
-- 24-05-2024 Added 1 optional text device for extensive forcast text.

--- Your settings ----------------------------------------------------------------
-- First set the used device index numbers and variables you might want to change.
---#################################################################
-- NB! To configure, set the devices you do NOT want to use to 99999
-- Easy to temporarily or permanently not use a device without rearanging the code.
---#################################################################
local wl_alarm_idx = 99999   --idx of the custom Alert device for Weather alarm.
local wl_thb_idx = 99999     --idx of the Temperature+humidity+barometer device adapter.
local wl_gtemp_idx = 99999   --idx of the Temperature device adapter.
local wl_wind_idx = 99999    --idx of the Wind device adapter.
local wl_zicht_idx = 99999   --idx of the Visibility device.
local wl_gr_idx = 99999      --idx of the Global Solar radiation device adapter.
local wl_verw_idx = 99999    --idx of the weatherforcast Text device.
local wl_rain_idx = 99999    --idx of the Rain forcast Text device.
local wl_txt_idx = 99999     --idx of the extensive forcast Text device. 
local wl_mintoD_idx = 99999    --idx of minTemp toDay Temperature device adapter.
local wl_maxtoD_idx = 99999    --idx of maxTemp toDay Temperature device adapter.
local wl_mintoM_idx = 99999    --idx of minTemp toMorrow Temperature device adapter.
local wl_maxtoM_idx = 99999    --idx of maxTemp toMorrow Temperature device adapter.

local wl_apikey = 'yourapikey' -- Access for weerlive.nl with your apikey.
local wl_icons_installed = 0    -- 1 or 0 when custom icons have been installed or not.

----------------------------------------------------------------------------------
return {
	on = {
		timer = { 
            --'every 2 minutes',  -- Only for testing.
            'every 10 minutes', 
        },
        httpResponses = {
		    'weerlive'       -- 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 = domoticz.LOG_INFO,
        --level = domoticz.LOG_DEBUG,
		marker = 'Weerlive-',
	},
	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

        -- Use some color variables
        local cGreen = '#008000;'
        local cYellow = '#ffff00;'
        local cOrange = '#ffa500;'
        local cRed = '#ff0000;'	
        local cWhite = '#FFFFFF;'
        local cBlue = '#0000FF;'
        local cGrey = '#8d8d8d;'

        local htmlColor = '#3b3bc4;'
        --Adjust lineheight
        local lineHeight = 1.2
        
        local weeralarm_value = dz.devices(wl_alarm_idx).sensorValue  -- Gets last stored value 
        local verw = ''     --Holds weather forcast
        local samenv = ''   --Holds weather summary
        local d1date = ''   --Holds date for today
        local d1nslperc = ''    --Holds neerslperc for today
        local d1znperc = ''     --Holds sun perc for today
        local d2date = ''       --Holds date for tomorrow
        local d2nslperc = ''    --Holds neerslperc for tomorrow
        local d2znperc = ''     --Holds sun perc for tomorrow
        local firstRain = 'norain'  -- Holds first rain hour.
        local lastRain = 'norain'   -- Holds first rain hour.
        local countRain = 0         -- Holds total expected rain in mm.
        local rainText = ''         -- Holds text to update rain forcast device.

        -- Local Functions go here =============
        local function gethumstat( lv )
            -- Calculate the humidity status.
            lv = tonumber( lv )
            if lv < 35 then return dz.HUM_DRY
                elseif lv >= 35 and lv < 45 then return dz.HUM_NORMAL
                elseif lv >= 45 and lv < 55 then return dz.HUM_COMFORTABLE
                elseif lv >= 55 and lv < 65 then return dz.HUM_NORMAL
                else return dz.HUM_WET
            end
        end
    
        local function getforecast( image ) 
            -- Define the BARO forcast.
            if image == "zonnig" or image == "helderenacht" then return dz.BARO_SUNNY
                elseif image == "halfbewolkt" or image == "mist" or image == "lichtbewolkt" or image == "nachtmist" then return dz.BARO_PARTLYCLOUDY
                elseif image == "bewolkt" or image == "zwaarbewolkt" or image == "wolkennacht" then return dz.BARO_CLOUDY
                elseif image == "regen" or image == 'halfbewolkt_regen' or image == "buien" or image == "hagel" or image == "sneeuw" then return dz.BARO_RAIN
                elseif image == "bliksem" then return dz.BARO_THUNDERSTORM
            else 
                return dz.BARO_NOINFO
            end
        end
            
        local function getIconNumber( image )
            if image == 'bewolkt' then iconNumber = 121 end
            if image == 'bliksem' then iconNumber = 122 end
            if image == 'hagel' then iconNumber = 123 end
            if image == 'halfbewolkt' then iconNumber = 124 end
            if image == 'helderenacht' then iconNumber = 125 end
            if image == 'lichtbewolkt' then iconNumber = 126 end
            if image == 'mist' then iconNumber = 127 end
            if image == 'nachtmist' then iconNumber = 128 end
            if image == 'regen' or image == 'buien' or image == 'halfbewolkt_regen' then iconNumber = 129 end
            if image == 'sneeuw' then iconNumber = 130 end
            if image == 'wolkennacht' or image == 'nachtbewolkt' then iconNumber = 131 end
            if image == 'zonnig' then iconNumber = 132 end
            if image == 'zwaarbewolkt' then iconNumber = 133 end
            -- For not existing image tekst called when no rain expected.
            if image == 'geenregen' then iconNumber = 134 end
            return iconNumber
        end

        local function getWindDirectionString( windDirection )
            -- To calculate the 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 = 'WNW' 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 ============
        -- Get the data.
		if (triggeredItem.isTimer) then
            -- Retrieve the data.
			dz.openURL({
				url = 'https://weerlive.nl/api/weerlive_api_v2.php?key=' .. wl_apikey .. '&locatie=' .. lat .. ',' .. long,
				method = 'GET',
				callback = 'weerlive'
			})
		end	

	    if (triggeredItem.isHTTPResponse) then
            -- Process the obtained data.
	        -- 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 )
                    
                    --- API ---------------------------------------
                    local api_table = result_table['api']
                    if type(api_table) == "table" then
                        dz.log( 'api_table: type = ' .. type(api_table), dz.LOG_DEBUG )
                        -- The data we get is:
                        -- "bron": "Bron: Weerdata KNMI/NOAA via Weerlive.nl",
                        -- "max_verz": 300,
                        -- "rest_verz": 90
                        local rest = api_table[1].rest_verz
                        dz.log( 'Remaining calls to weerlive.nl api for today = ' .. rest, dz.LOG_DEBUG )
                    else
                        dz.log( 'No api_table found', dz.LOG_ERROR )
                    end

                    --- LIVEWEER ---------------------------------
                    local liveweer_table = result_table['liveweer']
                    if type(liveweer_table) == "table" then
                        dz.log( 'liveweer_table: type = ' .. type(liveweer_table), dz.LOG_DEBUG )
                        -- The data we get is:
                        --"plaats": "Your City",
                        --"timestamp": 1714650484,
                        --"time": "02-05-2024 13:48:04",
                        --"temp": 23.2,
                        --"gtemp": 22.7,
                        --"samenv": "Licht bewolkt",
                        --"lv": 64,
                        --"windr": "O",
                        --"windrgr": 91.8,
                        --"windms": 2.14,
                        --"windbft": 2,
                        --"windknp": 4.2,
                        --"windkmh": 7.7,
                        --"luchtd": 998.851,
                        --"ldmmhg": 749,
                        --"dauwp": 16.1,
                        --"zicht": 27600,
                        --"gr": 796,
                        --"verw": "Vanaf vanmiddag onweersbuien. Vrijdag koeler weer met af en toe regen",
                        --"sup": "06:06",
                        --"sunder": "21:11",
                        --"image": "lichtbewolkt",
                        --"alarm": 0,
                        --"lkop": "Vanmiddag en vanavond plaatselijk overlast door onweersbuien",
                        --"ltekst": " Tot halverwege de middag zijn er geen waarschuwingen van kracht.Later vanmiddag en vanavond komen er vooral in het midden en zuiden onweersbuien voor. Deze buien kunnen plaatselijk gepaard gaan met windstoten, hagel en veel regen. Hierdoor kan er hinder zijn voor verkeer en buitenactiviteiten. Ook kan er plaatselijk wateroverlast zijn.Later in de avond neemt het aantal buien af en worden ze minder zwaar.Wat kan ik verwachten en wat kan ik doen?Meer detailsBij een onweersbui windstoten tot ca. 65 km/u. Hagel tot 2 cm. Daarnaast plaatselijk neerslaghoeveelheden van 20-40 mm in een uur.",
                        --"wrschklr": "groen",
                        --"wrsch_g": "02-05-2024 21:00",
                        --"wrsch_gts": 1714676400,
                        --"wrsch_gc": "geel"

                        -- TempHumBaro.
                        local temp = liveweer_table[1].temp -- Also used in with the wind device.
                        if wl_thb_idx ~= 99999 then 
                            local lv = liveweer_table[1].lv
                            local status = gethumstat( lv )
                            local luchtd = liveweer_table[1].luchtd
                            -- Just a quick side trip here for tomorrow's image
                            dz.log( 'Image text for tomorrow = '.. result_table.wk_verw[2].image, dz.LOG_DEBUG )
                            local image = result_table.wk_verw[2].image
                            local forecast = getforecast( image )
                            dz.log( 'Update TempHumBaro: Temp: ' .. temp .. ', Hum: ' .. lv .. ', Status: ' .. status .. ', Luchtdr: ' .. luchtd .. ', Forecast:' .. forecast, dz.LOG_DEBUG )
                            dz.devices(wl_thb_idx).updateTempHumBaro( temp, lv, status, luchtd, forecast )
                        end

                        -- Apparent temperature
                        local gtemp = liveweer_table[1].gtemp -- Also used in with the wind device.
                        if wl_gtemp_idx ~= 99999 then 
                            dz.log( 'Update gTemp: ' .. gtemp, dz.LOG_DEBUG )
                            dz.devices(wl_gtemp_idx).updateTemperature( gtemp )
                        end

                        -- Wind
                        if wl_wind_idx ~= 99999 then  
                            local windms = _u.round( liveweer_table[1].windms, 1 )
                            local windrgr = liveweer_table[1].windrgr
                            local windDirectionString = getWindDirectionString( windrgr )
                            --local windGust = result_table.current.wind_gusts_10m
                            dz.log( 'Update Windgr : ' .. windrgr .. ', Winddirection' .. windDirectionString .. ', Wind m/s: ' .. windms .. ', Gust: 0'  .. ', Temp: ' .. temp .. ', gTemp: ' .. gtemp, dz.LOG_DEBUG )
                            dz.devices(wl_wind_idx).updateWind( windrgr, windDirectionString, windms, 0, temp, gtemp )
                        end

                        -- Visibility
                        if wl_zicht_idx ~= 99999 then
                            local zicht = liveweer_table[1].zicht
                            dz.log( 'Update zicht: ' .. zicht / 1000, dz.LOG_DEBUG )
                            dz.devices(wl_zicht_idx).updateVisibility( zicht / 1000 )
                        end

                        -- Global radiation Watt/M2
                        if wl_gr_idx ~= 99999 then
                            local gr = liveweer_table[1].gr
                            dz.log( 'Update global radiation: ' .. gr, dz.LOG_DEBUG )
                            dz.devices(wl_gr_idx).updateRadiation( gr )                           
                        end
                        
                        -- Weatherforcast extensive text
                        if wl_txt_idx ~= 99999 then
                            local ltekst = liveweer_table[1].ltekst
                            --Space after sentences is often missing. Add a space to puntcation.
                            ltekst = string.gsub( ltekst, "(%.)", "%1 " )
                            ltekst = string.gsub( ltekst, "(%?)", "%1 " )
                            --Replace double spaces by one space.
                            --ltekst = string.gsub( ltekst, "%s%s", "%s" )
                            dz.log( 'Update extensive Text: ' .. ltekst, dz.LOG_DEBUG )
                            dz.devices(wl_txt_idx).updateText( '<span style="line-height:' .. lineHeight .. '">' .. ltekst  .. '</span>' )
                        end
                        
                        -- Weather alert
                        if wl_alarm_idx ~= 99999 then
                            local alarm = liveweer_table[1].alarm
                            local lkop = liveweer_table[1].lkop
                            local wrschklr = liveweer_table[1].wrschklr
                            local wrsch_g = liveweer_table[1].wrsch_g
                            local wrsch_gts = liveweer_table[1].wrsch_gts
                            local wrsch_gc = liveweer_table[1].wrsch_gc
                            local alertText = lkop

                            --Set the alert color
                            if wrschklr == 'rood' then
                                alertLevel = dz.ALERTLEVEL_RED
                            elseif wrschklr == 'oranje' then
                                alertLevel = dz.ALERTLEVEL_ORANGE
                            elseif wrschklr == 'geel' then
                                alertLevel = dz.ALERTLEVEL_YELLOW
                            else
                                alertLevel = dz.ALERTLEVEL_GREY
                            end

                            if wrsch_g ~= '-'  then
                                -- There is a Warning
                                if wrsch_gc == 'rood' then
                                    htmlbgColor = cRed
                                elseif wrsch_gc == 'oranje' then
                                    htmlbgColor = cOrange
                                elseif wrsch_gc == 'geel' then
                                    htmlbgColor = cYellow
                                else
                                    htmlbgColor = cWhite
                                end
                                -- Add warning time and message on new line with the warning color.
                                alertText = alertText .. '\n<span style="line-height:' .. lineHeight .. ';  background-color: ' .. htmlbgColor .. '">Vanaf ' .. wrsch_g .. ' geldt code ' .. wrsch_gc .. '.</span>'                                
                            end
                        
                            -- Update the alert device with new alertLevel and alertText.
                            dz.log( 'Update Weeralarm: ' .. alertText, dz.LOG_DEBUG )
                            dz.devices(wl_alarm_idx).updateAlertSensor(alertLevel, alertText )
                        end

                        -- Summary and forecast used for wl_verw_idx
                        samenv = liveweer_table[1].samenv
                        verw =  liveweer_table[1].verw

                    else
                        dz.log( 'No liveweer_table found', dz.LOG_ERROR )
                    end

                    ---- WK_VERW ----------------------------------
                    local wk_verw_table = result_table['wk_verw']
                    if type(wk_verw_table) == "table" then
                        dz.log( 'wk_verw_table: type = ' .. type(wk_verw_table), dz.LOG_DEBUG )
                        -- The data we get is for 5 days, including today:
                        --"dag": "12-05-2024",
                        --"image": "halfbewolkt",
                        --"max_temp": 18,
                        --"min_temp": 17,
                        --"windbft": 3,
                        --"windkmh": 18,
                        --"windknp": 10,
                        --"windms": 5,
                        --"windrgr": 121,
                        --"windr": "ZO",
                        --"neersl_perc_dag": 50,
                        --"zond_perc_dag": 68

                        d1date = string.sub( wk_verw_table[1].dag, 1, 5 )   -- We use only 'dd-mm'
                        d1nslperc = wk_verw_table[1].neersl_perc_dag
                        d1znperc = wk_verw_table[1].zond_perc_dag
                        d1mintemp = wk_verw_table[1].min_temp
                        d1maxtemp = wk_verw_table[1].max_temp
                            
                        d2date = string.sub( wk_verw_table[2].dag, 1, 5 )   -- We use only 'dd-mm'
                        d2nslperc = wk_verw_table[2].neersl_perc_dag 
                        d2znperc = wk_verw_table[2].zond_perc_dag
                        d2mintemp = wk_verw_table[2].min_temp
                        d2maxtemp = wk_verw_table[2].max_temp

                        
                        if wl_mintoD_idx ~= 99999 then
                            dz.log( 'Update Min. Temp toDay: ' .. d1mintemp, dz.LOG_DEBUG )
                            dz.devices(wl_mintoD_idx).updateTemperature( d1mintemp )
                        end
                        if wl_maxtoD_idx ~= 99999 then
                            dz.log( 'Update Max. Temp toDay: ' .. d1maxtemp, dz.LOG_DEBUG )
                            dz.devices(wl_maxtoD_idx).updateTemperature( d1maxtemp )
                        end
                        if wl_mintoM_idx ~= 99999 then
                            dz.log( 'Update Min. Temp toMorrow: ' .. d2mintemp, dz.LOG_DEBUG )
                            dz.devices(wl_mintoM_idx).updateTemperature( d2mintemp )
                        end
                        if wl_maxtoM_idx ~= 99999 then
                            dz.log( 'Update Max. Temp toMorrow: ' .. d2maxtemp, dz.LOG_DEBUG )
                            dz.devices(wl_maxtoM_idx).updateTemperature( d2maxtemp )
                        end

                        if wl_verw_idx ~= 99999 then
                            -- To calculate the expected sun hours.
                            local sunrise = dz.time.sunriseInMinutes
                            local sunset = dz.time.sunsetInMinutes
                            
                            -- For today -------------------------------------
                            local suntimeMinutes = ( d1znperc / 100 ) * ( sunset - sunrise ) 
                            local sunHours = suntimeMinutes / 60
                            local integerPart, fractionalPart = math.modf( sunHours )
                            fractionalPart = _u.round( fractionalPart * 60, 0 )
                            local sunMinutes = string.format( "%02d", fractionalPart )
                            local sunTime = integerPart .. ':' .. sunMinutes .. ' uur'
                            dz.log( 'Calculated sun time today = ' .. sunTime, dz.LOG_DEBUG )

                            local texta = ''
                            if wl_mintoD_idx ~= 99999 and wl_maxtoD_idx ~= 99999 then
                                texta = d1date .. ': Verwachte uren zon ' .. sunTime
                            else
                                -- arrowup = &#9650; arrowdown = &#9660; Degree celsius = &#8451;
                                texta = d1date .. ': <span style="color: ' ..  cGrey .. '">&#9660;</span>Temp. ' .. d1mintemp .. ' &#8451; - <span style="color: ' ..  cGrey .. '">&#9650;</span>Temp. ' .. d1maxtemp .. ' &#8451; - Zon ' .. sunTime
                            end
                            dz.log( texta, dz.LOG_DEBUG )

                            -- For tomorrow ----------------------------------
                            local suntimeMinutes = ( d2znperc / 100 ) * ( sunset - sunrise ) 
                            local sunHours = suntimeMinutes / 60
                            local integerPart, fractionalPart = math.modf( sunHours )
                            fractionalPart = _u.round( fractionalPart * 60, 0 )
                            local sunMinutes = string.format( "%02d", fractionalPart )
                            local sunTime = integerPart .. ':' .. sunMinutes .. ' uur'
                            dz.log( 'Calculated sun time tomorrow = ' .. sunTime, dz.LOG_DEBUG )

                            local textb = ''
                            if wl_mintoM_idx ~= 99999 and wl_maxtoM_idx ~= 99999 then
                                textb = d2date .. ': Verwachte uren zon ' .. sunTime
                            else
                                -- arrowup = &#9650; arrowdown = &#9660; Degree celsius = &#8451;
                                textb = d2date .. ': <span style="color: ' ..  cGrey .. '">&#9660;</span>Temp. ' .. d2mintemp .. ' &#8451; - <span style="color: ' ..  cGrey .. '">&#9650;</span>Temp. ' .. d2maxtemp .. ' &#8451; - Zon ' .. sunTime
                            end
                            dz.log( textb, dz.LOG_DEBUG )

                            -- Summary and forcast text.
                            dz.log( 'Samenvatting: ' .. samenv, dz.LOG_DEBUG ) 
                            dz.log( 'Verwachting: ' .. verw, dz.LOG_DEBUG )
                            local verw_text = '<span style="color: ' ..  htmlColor .. '">' .. samenv .. '\n' .. verw .. '</span>\n' .. texta .. '\n' .. textb

                            -- Update with new Text.
                            dz.log( 'Update Verwachting: ' .. verw_text, dz.LOG_DEBUG )
                            dz.devices(wl_verw_idx).updateText( verw_text )

                            if wl_icons_installed == 1 then
                                -- Update icon for tomorrow forecast.
                                local image = wk_verw_table[2].image
                                local iconNumber = getIconNumber( image )
                                dz.devices(wl_verw_idx).setIcon(iconNumber)
                            end
                        end                        
                    else
                        dz.log( 'No wk_verw_table found', dz.LOG_ERROR )
                    end

                    ---- UUR_VERW ----------------------------------
                    local uur_verw_table = result_table['uur_verw']
                    if type(uur_verw_table) == "table" then
                        dz.log( 'uur_verw_table: type = ' .. type(uur_verw_table), dz.LOG_DEBUG )
                        -- The data we get is for 24 hr, starting from next whole hour:
                        --"uur": "13-05-2024 21:00",
                        --"timestamp": 1715626800,
                        --"image": "wolkennacht",
                        --"temp": 20,
                        --"windbft": 2,
                        --"windkmh": 7,
                        --"windknp": 4,
                        --"windms": 2,
                        --"windrgr": 130,
                        --"windr": "ZO",
                        --"neersl": 0,
                        --"gr": 6
                       
                        if wl_rain_idx ~= 99999 then 
                            -- Loop through the table. Search for the first rain occurence.
                            local tc = #uur_verw_table
                            for i = 1, tc do                    
                                -- if found: Rain expexted at 'uur'
                                -- else: No rain expected until (last time found.)
                                local neersl = uur_verw_table[i].neersl
                                local hr = uur_verw_table[i].uur
                                if neersl ~= 0 then
                                    countRain = countRain + neersl
                                    local unixTime = dz.time.dateToTimestamp( hr, 'dd-mm-yyyy hh:MM' )
                                    if firstRain == 'norain' then firstRain = dz.time.timestampToDate( unixTime, 'hh:MM' , 0 ) end
                                    lastRain = dz.time.timestampToDate( unixTime, 'hh:MM' , 3600 ) -- is the next hour. 
                                else
                                    if firstRain ~= 'norain' then
                                        -- neersl == 0 after first episode of rain, so exit the loop.
                                        break
                                    end
                                end
                            end
                            if firstRain == 'norain' then
                                rainText = 'Geen neerslag verwacht < 24 uur'
                                dz.log( rainText, dz.LOG_DEBUG )
                            else
                                rainText = '<span style="color: ' ..  htmlColor .. '">1<sup>e</sup> Neerslag (totaal ' .. countRain .. ' l/m<sup>2</sup>) tussen ' .. firstRain .. ' - ' .. lastRain .. ' uur</span>'
                                dz.log( rainText, dz.LOG_DEBUG )
                            end
                            local rainTexta = d1date .. ': Kans op neerslag ' .. d1nslperc .. '%.'
                            dz.log( rainTexta, dz.LOG_DEBUG )
                            local rainTextb = d2date .. ': Kans op neerslag ' .. d2nslperc .. '%.'
                            dz.log( rainTextb, dz.LOG_DEBUG )

                             -- Update with new Text.
                            dz.log( 'Update Regenverwachting: ' .. rainText .. '\n' .. rainTexta .. '\n' .. rainTextb, dz.LOG_DEBUG )
                            dz.devices(wl_rain_idx).updateText( rainText .. '\n' .. rainTexta .. '\n' .. rainTextb )

                            if wl_icons_installed == 1 then
                                if rainText == 'Geen neerslag verwacht < 24 uur' then
                                    local iconNumber = getIconNumber( 'geenregen')
                                    dz.devices(wl_rain_idx).setIcon(iconNumber)
                                else
                                    -- Get rain icon
                                    local iconNumber = getIconNumber( 'regen' ) 
                                    dz.devices(wl_rain_idx).setIcon(iconNumber)
                                end
                            end
                        end
                    else
                        dz.log( 'No uur_verw_table found', dz.LOG_ERROR )
                    end
                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
}
-- That's All --------------------------------------------------
And, if you like, a slightly edited general weather icon from https://www.flaticon.com/icons
Icon_weersverwachting.zip
(6.34 KiB) Downloaded 89 times
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.
HvdW
Posts: 612
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by HvdW »

A nice addition and a beautiful icon for all situations.
I admire the work you put into it and enjoy the output.
Bugs bug me.
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

Updated script with some additions:

Code: Select all

-- 30-05-2024 Added call to function trimLRspaces in global_data.
--            Added wl_hideNowarning. When active (=1) hide device when there is no warning.
I look every day to an alert device and an extended text device both of which tell me there are no warnings.
I played with the option to hide a device from the script, which I had never done before, and I think I like it.
With the idea to show it only when there is a warning, I tried it.
So I added a variable that can be set for optional hiding of device when there is no warning to show.
If the variable is set to 1 and the "no warning texts" are present, the devices will be hidden. When the situation changes, then they become visible again.

Be aware that a refresh is required while you have the page open and the 'hidden status' changes.

Also I noticed varying behavior regarding text with leading or trailing spaces. For that I added a trim function under in global_data under helpers.

The function 'trimLRspaces' to put in in global_data:

Code: Select all

        trimLRspaces = function( s )
            return ( s:gsub( "^%s*(.-)%s*$", "%1" ) )
        end,

The script t-Weerlive:

Code: Select all

-- 12-05-2024 script by Jan Peppink, https://ict.peppink.nl
-- Started for weather alarm Expanded as renewed custom script instead of Buienradar.
-- Reduce the number of weather information sources.
--
-- Extra explaining comments and choices made ------------------------------------
-- Explanation of the api can be found at https://weerlive.nl/delen.php, where you also can obtain an api key.
-- Optional use of devices. You can skip them by giving them a dummy idx of 99999.
-- The BARO device forcast uses the 'image' text that is found for tomorrow.
-- WindGust is not yet part of weerlive api result. Hardcoded set to 0
-- Optional use of custom icons. Activate by setting wl_icons_installed = 1
--   NB. DOUBLE check function getIconNumber and correct your iconNumbers !!
-- The Weatherforcast device gets the icon that comes with forcast tomorrow.
--   May sometimes be a bit confusing with the text that is shown.
-- The Rainforcast device gets the icon rain or NO rain, when not expected < 24 hours.
-- The script translates mm rainfall to l/m2 which is easier to understand.
-- The script calculates expected sun hours given as zond_perc_dag of the time between sunrise and sunset to hh:MM.
-- An explanation about this can be found at https://www.knmi.nl/kennis-en-datacentrum/achtergrond/de-weersverwachting
-- 17-05-2024 Added 4 optional temperature devices for Min and Max Temp.
-- 24-05-2024 Added 1 optional text device for extensive forcast text.
-- 25-05-2024 Removed function 'getWindDirectionString'. Instead call _h.getDirectionfromDegree in global_data
-- 30-05-2024 Added call to function trimLRspaces in global_data.
--            Added wl_hideNowarning. When active (=1) hide device when there is no warning.


--- Your settings ----------------------------------------------------------------
-- First set the used device index numbers and variables you might want to change.
---#################################################################
-- NB! To configure, set the devices you do NOT want to use to 99999
-- Easy to temporarily or permanently not use a device without rearanging the code.
---#################################################################
local wl_alarm_idx = 99999   --idx of the custom Alert device for Weather alarm.
local wl_thb_idx = 99999  --idx of the Temperature+humidity+barometer device adapter.
local wl_gtemp_idx = 99999   --idx of the Temperature device adapter.
local wl_wind_idx = 99999    --idx of the Wind device adapter.
local wl_zicht_idx = 99999   --idx of the Visibility device.
local wl_gr_idx = 99999     --idx of the Global Solar radiation device adapter.
local wl_verw_idx = 99999    --idx of the weatherforcast Text device.
local wl_rain_idx = 99999    --idx of the Rain forcast Text device.
local wl_txt_idx = 99999     --idx of the extensive forcast Text device. 
local wl_mintoD_idx = 99999    --idx of minTemp toDay Temperature device adapter.
local wl_maxtoD_idx = 99999    --idx of maxTemp toDay Temperature device adapter.
local wl_mintoM_idx = 99999    --idx of minTemp toMorrow Temperature device adapter.
local wl_maxtoM_idx = 99999    --idx of maxTemp toMorrow Temperature device adapter.

local wl_apikey = 'yourapikey' -- Access for weerlive.nl with your apikey.
local wl_icons_installed = 0    -- 1 or 0 when custom icons have been installed or not.
local wl_hideNowarning = 0   -- 1 or 0 sets the device hidden when there is no warning.

----------------------------------------------------------------------------------
return {
	on = {
		timer = { 
           -- 'every 2 minutes',  -- Only for testing.
            'every 10 minutes', 
        },
        httpResponses = {
		    'weerlive'       -- 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 = domoticz.LOG_INFO,
        --level = domoticz.LOG_DEBUG,
		marker = 'Weerlive-',
	},
	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

        -- Use some color variables
        local cGreen = '#008000;'
        local cYellow = '#ffff00;'
        local cOrange = '#ffa500;'
        local cRed = '#ff0000;'	
        local cWhite = '#FFFFFF;'
        local cBlue = '#0000FF;'
        local cGrey = '#8d8d8d;'

        local htmlColor = '#3b3bc4;'
        --Adjust lineheight
        local lineHeight = 1.2
        
        local weeralarm_value = dz.devices(wl_alarm_idx).sensorValue  -- Gets last stored value 
        local verw = ''     --Holds weather forcast
        local samenv = ''   --Holds weather summary
        local d1date = ''   --Holds date for today
        local d1nslperc = ''    --Holds neerslperc for today
        local d1znperc = ''     --Holds sun perc for today
        local d2date = ''       --Holds date for tomorrow
        local d2nslperc = ''    --Holds neerslperc for tomorrow
        local d2znperc = ''     --Holds sun perc for tomorrow
        local firstRain = 'norain'  -- Holds first rain hour.
        local lastRain = 'norain'   -- Holds first rain hour.
        local countRain = 0         -- Holds total expected rain in mm.
        local rainText = ''         -- Holds text to update rain forcast device.

        -- Local Functions go here =============
        local function gethumstat( lv )
            -- Calculate the humidity status.
            lv = tonumber( lv )
            if lv < 35 then return dz.HUM_DRY
                elseif lv >= 35 and lv < 45 then return dz.HUM_NORMAL
                elseif lv >= 45 and lv < 55 then return dz.HUM_COMFORTABLE
                elseif lv >= 55 and lv < 65 then return dz.HUM_NORMAL
                else return dz.HUM_WET
            end
        end
    
        local function getforecast( image ) 
            -- Define the BARO forcast.
            if image == "zonnig" or image == "helderenacht" then return dz.BARO_SUNNY
                elseif image == "halfbewolkt" or image == "mist" or image == "lichtbewolkt" or image == "nachtmist" then return dz.BARO_PARTLYCLOUDY
                elseif image == "bewolkt" or image == "zwaarbewolkt" or image == "wolkennacht" then return dz.BARO_CLOUDY
                elseif image == "regen" or image == 'halfbewolkt_regen' or image == "buien" or image == "hagel" or image == "sneeuw" then return dz.BARO_RAIN
                elseif image == "bliksem" then return dz.BARO_THUNDERSTORM
            else 
                return dz.BARO_NOINFO
            end
        end
            
        local function getIconNumber( image )
            if image == 'bewolkt' then iconNumber = 121 end
            if image == 'bliksem' then iconNumber = 122 end
            if image == 'hagel' then iconNumber = 123 end
            if image == 'halfbewolkt' then iconNumber = 124 end
            if image == 'helderenacht' then iconNumber = 125 end
            if image == 'lichtbewolkt' then iconNumber = 126 end
            if image == 'mist' then iconNumber = 127 end
            if image == 'nachtmist' then iconNumber = 128 end
            if image == 'regen' or image == 'buien' or image == 'halfbewolkt_regen' then iconNumber = 129 end
            if image == 'sneeuw' then iconNumber = 130 end
            if image == 'wolkennacht' or image == 'nachtbewolkt' then iconNumber = 131 end
            if image == 'zonnig' then iconNumber = 132 end
            if image == 'zwaarbewolkt' then iconNumber = 133 end
            -- For not existing image tekst called when no rain expected.
            if image == 'geenregen' then iconNumber = 134 end
            return iconNumber
        end

        -- Now start to do something ============
        -- Get the data.
		if (triggeredItem.isTimer) then
            -- Retrieve the data.
			dz.openURL({
				url = 'https://weerlive.nl/api/weerlive_api_v2.php?key=' .. wl_apikey .. '&locatie=' .. lat .. ',' .. long,
				method = 'GET',
				callback = 'weerlive'
			})
		end	

	    if (triggeredItem.isHTTPResponse) then
            -- Process the obtained data.
	        -- 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 )
                    
                    --- API ---------------------------------------
                    local api_table = result_table['api']
                    if type(api_table) == "table" then
                        dz.log( 'api_table: type = ' .. type(api_table), dz.LOG_DEBUG )
                        -- The data we get is:
                        -- "bron": "Bron: Weerdata KNMI/NOAA via Weerlive.nl",
                        -- "max_verz": 300,
                        -- "rest_verz": 90
                        local rest = api_table[1].rest_verz
                        dz.log( 'Remaining calls to weerlive.nl api for today = ' .. rest, dz.LOG_DEBUG )
                    else
                        dz.log( 'No api_table found', dz.LOG_ERROR )
                    end

                    --- LIVEWEER ---------------------------------
                    local liveweer_table = result_table['liveweer']
                    if type(liveweer_table) == "table" then
                        dz.log( 'liveweer_table: type = ' .. type(liveweer_table), dz.LOG_DEBUG )
                        -- The data we get is:
                        --"plaats": "Your City",
                        --"timestamp": 1714650484,
                        --"time": "02-05-2024 13:48:04",
                        --"temp": 23.2,
                        --"gtemp": 22.7,
                        --"samenv": "Licht bewolkt",
                        --"lv": 64,
                        --"windr": "O",
                        --"windrgr": 91.8,
                        --"windms": 2.14,
                        --"windbft": 2,
                        --"windknp": 4.2,
                        --"windkmh": 7.7,
                        --"luchtd": 998.851,
                        --"ldmmhg": 749,
                        --"dauwp": 16.1,
                        --"zicht": 27600,
                        --"gr": 796,
                        --"verw": "Vanaf vanmiddag onweersbuien. Vrijdag koeler weer met af en toe regen",
                        --"sup": "06:06",
                        --"sunder": "21:11",
                        --"image": "lichtbewolkt",
                        --"alarm": 0,
                        --"lkop": "Vanmiddag en vanavond plaatselijk overlast door onweersbuien",
                        --"ltekst": " Tot halverwege de middag zijn er geen waarschuwingen van kracht.Later vanmiddag en vanavond komen er vooral in het midden en zuiden onweersbuien voor. Deze buien kunnen plaatselijk gepaard gaan met windstoten, hagel en veel regen. Hierdoor kan er hinder zijn voor verkeer en buitenactiviteiten. Ook kan er plaatselijk wateroverlast zijn.Later in de avond neemt het aantal buien af en worden ze minder zwaar.Wat kan ik verwachten en wat kan ik doen?Meer detailsBij een onweersbui windstoten tot ca. 65 km/u. Hagel tot 2 cm. Daarnaast plaatselijk neerslaghoeveelheden van 20-40 mm in een uur.",
                        --"wrschklr": "groen",
                        --"wrsch_g": "02-05-2024 21:00",
                        --"wrsch_gts": 1714676400,
                        --"wrsch_gc": "geel"

                        -- TempHumBaro.
                        local temp = liveweer_table[1].temp -- Also used in with the wind device.
                        if wl_thb_idx ~= 99999 then 
                            local lv = liveweer_table[1].lv
                            local status = gethumstat( lv )
                            local luchtd = liveweer_table[1].luchtd
                            -- Just a quick side trip here for tomorrow's image
                            dz.log( 'Image text for tomorrow = '.. result_table.wk_verw[2].image, dz.LOG_DEBUG )
                            local image = result_table.wk_verw[2].image
                            local forecast = getforecast( image )
                            dz.log( 'Update TempHumBaro: Temp: ' .. temp .. ', Hum: ' .. lv .. ', Status: ' .. status .. ', Luchtdr: ' .. luchtd .. ', Forecast:' .. forecast, dz.LOG_DEBUG )
                            dz.devices(wl_thb_idx).updateTempHumBaro( temp, lv, status, luchtd, forecast )
                        end

                        -- Apparent temperature
                        local gtemp = liveweer_table[1].gtemp -- Also used in with the wind device.
                        if wl_gtemp_idx ~= 99999 then 
                            dz.log( 'Update gTemp: ' .. gtemp, dz.LOG_DEBUG )
                            dz.devices(wl_gtemp_idx).updateTemperature( gtemp )
                        end

                        -- Wind
                        if wl_wind_idx ~= 99999 then  
                            local windms = _u.round( liveweer_table[1].windms, 1 )
                            local windrgr = liveweer_table[1].windrgr
                            local windDirectionString = _h.getDirectionfromDegree( windrgr )
                            --local windGust = result_table.current.wind_gusts_10m
                            dz.log( 'Update Windgr : ' .. windrgr .. ', Winddirection' .. windDirectionString .. ', Wind m/s: ' .. windms .. ', Gust: 0'  .. ', Temp: ' .. temp .. ', gTemp: ' .. gtemp, dz.LOG_DEBUG )
                            dz.devices(wl_wind_idx).updateWind( windrgr, windDirectionString, windms, 0, temp, gtemp )
                        end

                        -- Visibility
                        if wl_zicht_idx ~= 99999 then
                            local zicht = liveweer_table[1].zicht
                            dz.log( 'Update zicht: ' .. zicht / 1000, dz.LOG_DEBUG )
                            dz.devices(wl_zicht_idx).updateVisibility( zicht / 1000 )
                        end

                        -- Global radiation Watt/M2
                        if wl_gr_idx ~= 99999 then
                            local gr = liveweer_table[1].gr
                            dz.log( 'Update global radiation: ' .. gr, dz.LOG_DEBUG )
                            dz.devices(wl_gr_idx).updateRadiation( gr )                           
                        end
                        
                        -- Weatherforcast extensive text
                        if wl_txt_idx ~= 99999 then
                            local ltekst = liveweer_table[1].ltekst
                            --Space after sentences is often missing. Add a space to puntcation.
                            ltekst = string.gsub( ltekst, "(%.)", "%1 " )
                            ltekst = string.gsub( ltekst, "(%?)", "%1 " )
                            ltekst = string.gsub( ltekst, "etails", "etails " )
                            ltekst = string.gsub( ltekst, "etails :", "etails: " )
                            ltekst = string.gsub( ltekst, "etails ", "etails: " )
                            ltekst = string.gsub( ltekst, "  ", " " )
                            ltekst = _h.trimLRspaces( ltekst )
                            --Replace double spaces by one space.
                            --ltekst = string.gsub( ltekst, "%s%s", "%s" )
                            dz.log( 'Update extensive Text: "' .. ltekst .. '"', dz.LOG_DEBUG )
                            dz.devices(wl_txt_idx).updateText( '<span style="line-height:' .. lineHeight .. '">' .. ltekst  .. '</span>' )
                            
                            if wl_hideNowarning == 1 and ltekst == 'Er zijn momenteel geen waarschuwingen van kracht.' then
                                -- set hidden.
                                if string.sub( dz.devices(wl_txt_idx).name, 1, 1) ~= '$' then
                                    dz.log( 'Set ' .. dz.devices(wl_txt_idx).name .. ' hidden!', dz.LOG_DEBUG )
                                    dz.devices(wl_txt_idx).rename( '$' .. dz.devices(wl_txt_idx).name )
                                end
                            else
                                -- set visiblle
                                if string.sub( dz.devices(wl_txt_idx).name, 1, 1) == '$' then
                                    dz.log( 'Set ' .. dz.devices(wl_txt_idx).name .. ' visible!', dz.LOG_DEBUG )
                                    dz.devices(wl_txt_idx).rename( string.gsub( dz.devices(wl_txt_idx).name, '%$', '') )
                                end
                            end
                        end
                        
                        -- Weather alert
                        if wl_alarm_idx ~= 99999 then
                            local alarm = liveweer_table[1].alarm
                            local lkop = liveweer_table[1].lkop
                            local wrschklr = liveweer_table[1].wrschklr
                            local wrsch_g = liveweer_table[1].wrsch_g
                            local wrsch_gts = liveweer_table[1].wrsch_gts
                            local wrsch_gc = liveweer_table[1].wrsch_gc
                            local alertText = lkop

                            --Set the alert color
                            if wrschklr == 'rood' then
                                alertLevel = dz.ALERTLEVEL_RED
                            elseif wrschklr == 'oranje' then
                                alertLevel = dz.ALERTLEVEL_ORANGE
                            elseif wrschklr == 'geel' then
                                alertLevel = dz.ALERTLEVEL_YELLOW
                            else
                                alertLevel = dz.ALERTLEVEL_GREY
                            end

                            if wrsch_g ~= '-'  then
                                -- There is a Warning
                                if wrsch_gc == 'rood' then
                                    htmlbgColor = cRed
                                elseif wrsch_gc == 'oranje' then
                                    htmlbgColor = cOrange
                                elseif wrsch_gc == 'geel' then
                                    htmlbgColor = cYellow
                                else
                                    htmlbgColor = cWhite
                                end
                                -- Add warning time and message on new line with the warning color.
                                alertText = alertText .. '\n<span style="line-height:' .. lineHeight .. ';  background-color: ' .. htmlbgColor .. '">Vanaf ' .. wrsch_g .. ' geldt code ' .. wrsch_gc .. '.</span>'                                
                            end
                        
                            -- Update the alert device with new alertLevel and alertText.
                            dz.log( 'Update Weeralarm: ' .. alertText, dz.LOG_DEBUG )
                            dz.devices(wl_alarm_idx).updateAlertSensor(alertLevel, alertText )
                            
                            if wl_hideNowarning == 1 and lkop == 'Er zijn geen waarschuwingen' and wrsch_g == '-' and wrsch_gc == '-' then
                                -- set hidden.
                                if string.sub( dz.devices(wl_alarm_idx).name, 1, 1) ~= '$' then
                                    dz.log( 'Set ' .. dz.devices(wl_alarm_idx).name .. ' hidden!', dz.LOG_DEBUG )
                                    dz.devices(wl_alarm_idx).rename( '$' .. dz.devices(wl_alarm_idx).name )
                                end
                            else
                                -- set visiblle
                                if string.sub( dz.devices(wl_alarm_idx).name, 1, 1) == '$' then
                                    dz.log( 'Set ' .. dz.devices(wl_alarm_idx).name .. ' visible!', dz.LOG_DEBUG )
                                    dz.devices(wl_alarm_idx).rename( string.gsub( dz.devices(wl_alarm_idx).name, '%$', '') )
                                end
                            end                            
                        end

                        -- Summary and forecast used for wl_verw_idx
                        samenv = liveweer_table[1].samenv
                        verw =  liveweer_table[1].verw

                    else
                        dz.log( 'No liveweer_table found', dz.LOG_ERROR )
                    end

                    ---- WK_VERW ----------------------------------
                    local wk_verw_table = result_table['wk_verw']
                    if type(wk_verw_table) == "table" then
                        dz.log( 'wk_verw_table: type = ' .. type(wk_verw_table), dz.LOG_DEBUG )
                        -- The data we get is for 5 days, including today:
                        --"dag": "12-05-2024",
                        --"image": "halfbewolkt",
                        --"max_temp": 18,
                        --"min_temp": 17,
                        --"windbft": 3,
                        --"windkmh": 18,
                        --"windknp": 10,
                        --"windms": 5,
                        --"windrgr": 121,
                        --"windr": "ZO",
                        --"neersl_perc_dag": 50,
                        --"zond_perc_dag": 68

                        d1date = string.sub( wk_verw_table[1].dag, 1, 5 )   -- We use only 'dd-mm'
                        d1nslperc = wk_verw_table[1].neersl_perc_dag
                        d1znperc = wk_verw_table[1].zond_perc_dag
                        d1mintemp = wk_verw_table[1].min_temp
                        d1maxtemp = wk_verw_table[1].max_temp
                            
                        d2date = string.sub( wk_verw_table[2].dag, 1, 5 )   -- We use only 'dd-mm'
                        d2nslperc = wk_verw_table[2].neersl_perc_dag 
                        d2znperc = wk_verw_table[2].zond_perc_dag
                        d2mintemp = wk_verw_table[2].min_temp
                        d2maxtemp = wk_verw_table[2].max_temp

                        
                        if wl_mintoD_idx ~= 99999 then
                            dz.log( 'Update Min. Temp toDay: ' .. d1mintemp, dz.LOG_DEBUG )
                            dz.devices(wl_mintoD_idx).updateTemperature( d1mintemp )
                        end
                        if wl_maxtoD_idx ~= 99999 then
                            dz.log( 'Update Max. Temp toDay: ' .. d1maxtemp, dz.LOG_DEBUG )
                            dz.devices(wl_maxtoD_idx).updateTemperature( d1maxtemp )
                        end
                        if wl_mintoM_idx ~= 99999 then
                            dz.log( 'Update Min. Temp toMorrow: ' .. d2mintemp, dz.LOG_DEBUG )
                            dz.devices(wl_mintoM_idx).updateTemperature( d2mintemp )
                        end
                        if wl_maxtoM_idx ~= 99999 then
                            dz.log( 'Update Max. Temp toMorrow: ' .. d2maxtemp, dz.LOG_DEBUG )
                            dz.devices(wl_maxtoM_idx).updateTemperature( d2maxtemp )
                        end

                        if wl_verw_idx ~= 99999 then
                            -- To calculate the expected sun hours.
                            local sunrise = dz.time.sunriseInMinutes
                            local sunset = dz.time.sunsetInMinutes
                            
                            -- For today -------------------------------------
                            local suntimeMinutes = ( d1znperc / 100 ) * ( sunset - sunrise ) 
                            local sunHours = suntimeMinutes / 60
                            local integerPart, fractionalPart = math.modf( sunHours )
                            fractionalPart = _u.round( fractionalPart * 60, 0 )
                            local sunMinutes = string.format( "%02d", fractionalPart )
                            local sunTime = integerPart .. ':' .. sunMinutes .. ' uur'
                            dz.log( 'Calculated sun time today = ' .. sunTime, dz.LOG_DEBUG )

                            local texta = ''
                            if wl_mintoD_idx ~= 99999 and wl_maxtoD_idx ~= 99999 then
                                texta = d1date .. ': Verwachte uren zon ' .. sunTime
                            else
                                -- arrowup = &#9650; arrowdown = &#9660; Degree celsius = &#8451;
                                texta = d1date .. ': <span style="color: ' ..  cGrey .. '">&#9660;</span>Temp. ' .. d1mintemp .. ' &#8451; - <span style="color: ' ..  cGrey .. '">&#9650;</span>Temp. ' .. d1maxtemp .. ' &#8451; - Zon ' .. sunTime
                            end
                            dz.log( texta, dz.LOG_DEBUG )

                            -- For tomorrow ----------------------------------
                            local suntimeMinutes = ( d2znperc / 100 ) * ( sunset - sunrise ) 
                            local sunHours = suntimeMinutes / 60
                            local integerPart, fractionalPart = math.modf( sunHours )
                            fractionalPart = _u.round( fractionalPart * 60, 0 )
                            local sunMinutes = string.format( "%02d", fractionalPart )
                            local sunTime = integerPart .. ':' .. sunMinutes .. ' uur'
                            dz.log( 'Calculated sun time tomorrow = ' .. sunTime, dz.LOG_DEBUG )

                            local textb = ''
                            if wl_mintoM_idx ~= 99999 and wl_maxtoM_idx ~= 99999 then
                                textb = d2date .. ': Verwachte uren zon ' .. sunTime
                            else
                                -- arrowup = &#9650; arrowdown = &#9660; Degree celsius = &#8451;
                                textb = d2date .. ': <span style="color: ' ..  cGrey .. '">&#9660;</span>Temp. ' .. d2mintemp .. ' &#8451; - <span style="color: ' ..  cGrey .. '">&#9650;</span>Temp. ' .. d2maxtemp .. ' &#8451; - Zon ' .. sunTime
                            end
                            dz.log( textb, dz.LOG_DEBUG )

                            -- Summary and forcast text.
                            dz.log( 'Samenvatting: ' .. samenv, dz.LOG_DEBUG ) 
                            dz.log( 'Verwachting: ' .. verw, dz.LOG_DEBUG )
                            local verw_text = '<span style="color: ' ..  htmlColor .. '">' .. samenv .. '\n' .. verw .. '</span>\n' .. texta .. '\n' .. textb

                            -- Update with new Text.
                            dz.log( 'Update Verwachting: ' .. verw_text, dz.LOG_DEBUG )
                            dz.devices(wl_verw_idx).updateText( verw_text )

                            if wl_icons_installed == 1 then
                                -- Update icon for tomorrow forecast.
                                local image = wk_verw_table[2].image
                                local iconNumber = getIconNumber( image )
                                dz.devices(wl_verw_idx).setIcon(iconNumber)
                            end
                        end                        
                    else
                        dz.log( 'No wk_verw_table found', dz.LOG_ERROR )
                    end

                    ---- UUR_VERW ----------------------------------
                    local uur_verw_table = result_table['uur_verw']
                    if type(uur_verw_table) == "table" then
                        dz.log( 'uur_verw_table: type = ' .. type(uur_verw_table), dz.LOG_DEBUG )
                        -- The data we get is for 24 hr, starting from next whole hour:
                        --"uur": "13-05-2024 21:00",
                        --"timestamp": 1715626800,
                        --"image": "wolkennacht",
                        --"temp": 20,
                        --"windbft": 2,
                        --"windkmh": 7,
                        --"windknp": 4,
                        --"windms": 2,
                        --"windrgr": 130,
                        --"windr": "ZO",
                        --"neersl": 0,
                        --"gr": 6
                       
                        if wl_rain_idx ~= 99999 then 
                            -- Loop through the table. Search for the first rain occurence.
                            local tc = #uur_verw_table
                            for i = 1, tc do                    
                                -- if found: Rain expexted at 'uur'
                                -- else: No rain expected until (last time found.)
                                local neersl = uur_verw_table[i].neersl
                                local hr = uur_verw_table[i].uur
                                if neersl ~= 0 then
                                    countRain = countRain + neersl
                                    local unixTime = dz.time.dateToTimestamp( hr, 'dd-mm-yyyy hh:MM' )
                                    if firstRain == 'norain' then firstRain = dz.time.timestampToDate( unixTime, 'hh:MM' , 0 ) end
                                    lastRain = dz.time.timestampToDate( unixTime, 'hh:MM' , 3600 ) -- is the next hour. 
                                else
                                    if firstRain ~= 'norain' then
                                        -- neersl == 0 after first episode of rain, so exit the loop.
                                        break
                                    end
                                end
                            end
                            if firstRain == 'norain' then
                                rainText = 'Geen neerslag verwacht < 24 uur'
                                dz.log( rainText, dz.LOG_DEBUG )
                            else
                                rainText = '<span style="color: ' ..  htmlColor .. '">1<sup>e</sup> Neerslag (totaal ' .. countRain .. ' l/m<sup>2</sup>) tussen ' .. firstRain .. ' - ' .. lastRain .. ' uur</span>'
                                dz.log( rainText, dz.LOG_DEBUG )
                            end
                            local rainTexta = d1date .. ': Kans op neerslag ' .. d1nslperc .. '%.'
                            dz.log( rainTexta, dz.LOG_DEBUG )
                            local rainTextb = d2date .. ': Kans op neerslag ' .. d2nslperc .. '%.'
                            dz.log( rainTextb, dz.LOG_DEBUG )

                             -- Update with new Text.
                            dz.log( 'Update Regenverwachting: ' .. rainText .. '\n' .. rainTexta .. '\n' .. rainTextb, dz.LOG_DEBUG )
                            dz.devices(wl_rain_idx).updateText( rainText .. '\n' .. rainTexta .. '\n' .. rainTextb )

                            if wl_icons_installed == 1 then
                                if rainText == 'Geen neerslag verwacht < 24 uur' then
                                    local iconNumber = getIconNumber( 'geenregen')
                                    dz.devices(wl_rain_idx).setIcon(iconNumber)
                                else
                                    -- Get rain icon
                                    local iconNumber = getIconNumber( 'regen' ) 
                                    dz.devices(wl_rain_idx).setIcon(iconNumber)
                                end
                            end
                        end
                    else
                        dz.log( 'No uur_verw_table found', dz.LOG_ERROR )
                    end
                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
}
-- 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.
janpep
Posts: 270
Joined: Thursday 14 March 2024 10:11
Target OS: Linux
Domoticz version: 2025.1
Location: Netherlands
Contact:

Re: Configurable 'KNMI Weerlive' script

Post by janpep »

I updated the script with optional link to the weather forcast with more information and radar for the location:

Code: Select all

-- 27-09-2024 Added optional link to forcast of city. Find this link at https://weerlive.nl/plaatsnamen.php
Therefore added two variables.

Code: Select all

local wl_locationurl = 'https://weerlive.nl/weer.php?plaatsnaam=<yourcityasfoundatweerlive>'
local wl_uselocationurl = 1             -- 1 or 0 when wl_url is used or not.
The link is added to the first line of the forecast.
Weerlive-9-JanPep.png
Weerlive-9-JanPep.png (14.13 KiB) Viewed 3053 times
In my case a link in the first line to: https://weerlive.nl/weer.php?plaatsnaam=Alphen aan den Rijn

The modified script:

Code: Select all

-- 12-05-2024 script by Jan Peppink, https://ict.peppink.nl
-- Started for weather alarm Expanded as renewed custom script instead of Buienradar.
-- Reduce the number of weather information sources.
--
-- Extra explaining comments and choices made ------------------------------------
-- Explanation of the api can be found at https://weerlive.nl/delen.php, where you also can obtain an api key.
-- Optional use of devices. You can skip them by giving them a dummy idx of 99999.
-- The BARO device forcast uses the 'image' text that is found for tomorrow.
-- WindGust is not yet part of weerlive api result. Hardcoded set to 0
-- Optional use of custom icons. Activate by setting wl_icons_installed = 1
--   NB. DOUBLE check function getIconNumber and correct your iconNumbers !!
-- The Weatherforcast device gets the icon that comes with forcast tomorrow.
--   May sometimes be a bit confusing with the text that is shown.
-- The Rainforcast device gets the icon rain or NO rain, when not expected < 24 hours.
-- The script translates mm rainfall to l/m2 which is easier to understand.
-- The script calculates expected sun hours given as zond_perc_dag of the time between sunrise and sunset to hh:MM.
-- An explanation about this can be found at https://www.knmi.nl/kennis-en-datacentrum/achtergrond/de-weersverwachting
-- 17-05-2024 Added 4 optional temperature devices for Min and Max Temp.
-- 24-05-2024 Added 1 optional text device for extensive forcast text.
-- 25-05-2024 Removed function 'getWindDirectionString'. Instead call _h.getDirectionfromDegree in global_data
-- 30-05-2024 Added call to function trimLRspaces in global_data.
--            Added wl_hideNowarning. When active (=1) hide device when there is no warning.
-- 27-09-2024 Added optional link to forcast of city. Find this link at https://weerlive.nl/plaatsnamen.php


--- Your settings ----------------------------------------------------------------
-- First set the used device index numbers and variables you might want to change.
---#################################################################
-- NB! To configure, set the devices you do NOT want to use to 99999
-- Easy to temporarily or permanently not use a device without rearanging the code.
---#################################################################
local wl_alarm_idx = 99999   --idx of the custom Alert device for Weather alarm.
local wl_thb_idx = 99999     --idx of the Temperature+humidity+barometer device adapter.
local wl_gtemp_idx = 99999   --idx of the Temperature device adapter.
local wl_wind_idx = 99999    --idx of the Wind device adapter.
local wl_zicht_idx = 99999   --idx of the Visibility device.
local wl_gr_idx = 99999      --idx of the Global Solar radiation device adapter.
local wl_verw_idx = 99999    --idx of the weatherforcast Text device.
local wl_rain_idx = 99999    --idx of the Rain forcast Text device.
local wl_txt_idx = 99999     --idx of the extensive forcast Text device. 
local wl_mintoD_idx = 99999    --idx of minTemp toDay Temperature device adapter.
local wl_maxtoD_idx = 99999    --idx of maxTemp toDay Temperature device adapter.
local wl_mintoM_idx = 99999    --idx of minTemp toMorrow Temperature device adapter.
local wl_maxtoM_idx = 99999    --idx of maxTemp toMorrow Temperature device adapter.

local wl_apikey = '<yourapikey>' -- Access for weerlive.nl with your apikey.
local wl_locationurl = 'https://weerlive.nl/weer.php?plaatsnaam=<yourcityasfoundatweerlive>'
local wl_uselocationurl = 1             -- 1 or 0 when wl_url is used or not.
local wl_icons_installed = 1    -- 1 or 0 when custom icons have been installed or not.
local wl_hideNowarning = 1   -- 1 or 0 sets the device hidden when there is no warning.

----------------------------------------------------------------------------------
return {
	on = {
		timer = { 
            --'every 2 minutes',  -- Only for testing.
            'every 10 minutes', 
        },
        httpResponses = {
		    'weerlive'       -- matches callback string below
        },
	},
	logging = {
		-- 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 = 'Weerlive-',
	},
	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

        -- Use some color variables
        local cGreen = '#008000;'
        local cYellow = '#ffff00;'
        local cOrange = '#ffa500;'
        local cRed = '#ff0000;'	
        local cWhite = '#FFFFFF;'
        local cBlue = '#0000FF;'
        local cGrey = '#8d8d8d;'

        local htmlColor = '#3b3bc4;'
        --Adjust lineheight
        local lineHeight = 1.2
        
        local weeralarm_value = dz.devices(wl_alarm_idx).sensorValue  -- Gets last stored value 
        local verw = ''     --Holds weather forcast
        local samenv = ''   --Holds weather summary
        local d1date = ''   --Holds date for today
        local d1nslperc = ''    --Holds neerslperc for today
        local d1znperc = ''     --Holds sun perc for today
        local d2date = ''       --Holds date for tomorrow
        local d2nslperc = ''    --Holds neerslperc for tomorrow
        local d2znperc = ''     --Holds sun perc for tomorrow
        local firstRain = 'norain'  -- Holds first rain hour.
        local lastRain = 'norain'   -- Holds first rain hour.
        local countRain = 0         -- Holds total expected rain in mm.
        local rainText = ''         -- Holds text to update rain forcast device.

        -- Local Functions go here =============
        local function gethumstat( lv )
            -- Calculate the humidity status.
            lv = tonumber( lv )
            if lv < 35 then return dz.HUM_DRY
                elseif lv >= 35 and lv < 45 then return dz.HUM_NORMAL
                elseif lv >= 45 and lv < 55 then return dz.HUM_COMFORTABLE
                elseif lv >= 55 and lv < 65 then return dz.HUM_NORMAL
                else return dz.HUM_WET
            end
        end
    
        local function getforecast( image ) 
            -- Define the BARO forcast.
            if image == "zonnig" or image == "helderenacht" then return dz.BARO_SUNNY
                elseif image == "halfbewolkt" or image == "mist" or image == "lichtbewolkt" or image == "nachtmist" then return dz.BARO_PARTLYCLOUDY
                elseif image == "bewolkt" or image == "zwaarbewolkt" or image == "wolkennacht" then return dz.BARO_CLOUDY
                elseif image == "regen" or image == 'halfbewolkt_regen' or image == "buien" or image == "hagel" or image == "sneeuw" then return dz.BARO_RAIN
                elseif image == "bliksem" then return dz.BARO_THUNDERSTORM
            else 
                return dz.BARO_NOINFO
            end
        end
            
        local function getIconNumber( image )
            if image == 'bewolkt' then iconNumber = 121 end
            if image == 'bliksem' then iconNumber = 122 end
            if image == 'hagel' then iconNumber = 123 end
            if image == 'halfbewolkt' then iconNumber = 124 end
            if image == 'helderenacht' then iconNumber = 125 end
            if image == 'lichtbewolkt' then iconNumber = 126 end
            if image == 'mist' then iconNumber = 127 end
            if image == 'nachtmist' then iconNumber = 128 end
            if image == 'regen' or image == 'buien' or image == 'halfbewolkt_regen' then iconNumber = 129 end
            if image == 'sneeuw' then iconNumber = 130 end
            if image == 'wolkennacht' or image == 'nachtbewolkt' then iconNumber = 131 end
            if image == 'zonnig' then iconNumber = 132 end
            if image == 'zwaarbewolkt' then iconNumber = 133 end
            -- For not existing image tekst called when no rain expected.
            if image == 'geenregen' then iconNumber = 134 end
            return iconNumber
        end

        -- Now start to do something ============
        -- Get the data.
		if (triggeredItem.isTimer) then
            -- Retrieve the data.
			dz.openURL({
				url = 'https://weerlive.nl/api/weerlive_api_v2.php?key=' .. wl_apikey .. '&locatie=' .. lat .. ',' .. long,
				method = 'GET',
				callback = 'weerlive'
			})
		end	

	    if (triggeredItem.isHTTPResponse) then
            -- Process the obtained data.
	        -- 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 )
                    
                    --- API ---------------------------------------
                    local api_table = result_table['api']
                    if type(api_table) == "table" then
                        dz.log( 'api_table: type = ' .. type(api_table), dz.LOG_DEBUG )
                        -- The data we get is:
                        -- "bron": "Bron: Weerdata KNMI/NOAA via Weerlive.nl",
                        -- "max_verz": 300,
                        -- "rest_verz": 90
                        local rest = api_table[1].rest_verz
                        dz.log( 'Remaining calls to weerlive.nl api for today = ' .. rest, dz.LOG_DEBUG )
                    else
                        dz.log( 'No api_table found', dz.LOG_ERROR )
                    end

                    --- LIVEWEER ---------------------------------
                    local liveweer_table = result_table['liveweer']
                    if type(liveweer_table) == "table" then
                        dz.log( 'liveweer_table: type = ' .. type(liveweer_table), dz.LOG_DEBUG )
                        -- The data we get is:
                        --"plaats": "Your City",
                        --"timestamp": 1714650484,
                        --"time": "02-05-2024 13:48:04",
                        --"temp": 23.2,
                        --"gtemp": 22.7,
                        --"samenv": "Licht bewolkt",
                        --"lv": 64,
                        --"windr": "O",
                        --"windrgr": 91.8,
                        --"windms": 2.14,
                        --"windbft": 2,
                        --"windknp": 4.2,
                        --"windkmh": 7.7,
                        --"luchtd": 998.851,
                        --"ldmmhg": 749,
                        --"dauwp": 16.1,
                        --"zicht": 27600,
                        --"gr": 796,
                        --"verw": "Vanaf vanmiddag onweersbuien. Vrijdag koeler weer met af en toe regen",
                        --"sup": "06:06",
                        --"sunder": "21:11",
                        --"image": "lichtbewolkt",
                        --"alarm": 0,
                        --"lkop": "Vanmiddag en vanavond plaatselijk overlast door onweersbuien",
                        --"ltekst": " Tot halverwege de middag zijn er geen waarschuwingen van kracht.Later vanmiddag en vanavond komen er vooral in het midden en zuiden onweersbuien voor. Deze buien kunnen plaatselijk gepaard gaan met windstoten, hagel en veel regen. Hierdoor kan er hinder zijn voor verkeer en buitenactiviteiten. Ook kan er plaatselijk wateroverlast zijn.Later in de avond neemt het aantal buien af en worden ze minder zwaar.Wat kan ik verwachten en wat kan ik doen?Meer detailsBij een onweersbui windstoten tot ca. 65 km/u. Hagel tot 2 cm. Daarnaast plaatselijk neerslaghoeveelheden van 20-40 mm in een uur.",
                        --"wrschklr": "groen",
                        --"wrsch_g": "02-05-2024 21:00",
                        --"wrsch_gts": 1714676400,
                        --"wrsch_gc": "geel"

                        -- TempHumBaro.
                        local temp = liveweer_table[1].temp -- Also used in with the wind device.
                        if wl_thb_idx ~= 99999 then 
                            local lv = liveweer_table[1].lv
                            local status = gethumstat( lv )
                            local luchtd = liveweer_table[1].luchtd
                            -- Just a quick side trip here for tomorrow's image
                            dz.log( 'Image text for tomorrow = '.. result_table.wk_verw[2].image, dz.LOG_DEBUG )
                            local image = result_table.wk_verw[2].image
                            local forecast = getforecast( image )
                            dz.log( 'Update TempHumBaro: Temp: ' .. temp .. ', Hum: ' .. lv .. ', Status: ' .. status .. ', Luchtdr: ' .. luchtd .. ', Forecast:' .. forecast, dz.LOG_DEBUG )
                            dz.devices(wl_thb_idx).updateTempHumBaro( temp, lv, status, luchtd, forecast )
                        end

                        -- Apparent temperature
                        local gtemp = liveweer_table[1].gtemp -- Also used in with the wind device.
                        if wl_gtemp_idx ~= 99999 then 
                            dz.log( 'Update gTemp: ' .. gtemp, dz.LOG_DEBUG )
                            dz.devices(wl_gtemp_idx).updateTemperature( gtemp )
                        end

                        -- Wind
                        if wl_wind_idx ~= 99999 then  
                            local windms = _u.round( liveweer_table[1].windms, 1 )
                            local windrgr = liveweer_table[1].windrgr
                            local windDirectionString = _h.getDirectionfromDegree( windrgr )
                            --local windGust = result_table.current.wind_gusts_10m
                            dz.log( 'Update Windgr : ' .. windrgr .. ', Winddirection' .. windDirectionString .. ', Wind m/s: ' .. windms .. ', Gust: 0'  .. ', Temp: ' .. temp .. ', gTemp: ' .. gtemp, dz.LOG_DEBUG )
                            dz.devices(wl_wind_idx).updateWind( windrgr, windDirectionString, windms, 0, temp, gtemp )
                        end

                        -- Visibility
                        if wl_zicht_idx ~= 99999 then
                            local zicht = liveweer_table[1].zicht
                            dz.log( 'Update zicht: ' .. zicht / 1000, dz.LOG_DEBUG )
                            dz.devices(wl_zicht_idx).updateVisibility( zicht / 1000 )
                        end

                        -- Global radiation Watt/M2
                        if wl_gr_idx ~= 99999 then
                            local gr = liveweer_table[1].gr
                            dz.log( 'Update global radiation: ' .. gr, dz.LOG_DEBUG )
                            dz.devices(wl_gr_idx).updateRadiation( gr )                           
                        end
                        
                        -- Weatherforcast extensive text
                        if wl_txt_idx ~= 99999 then
                            local ltekst = liveweer_table[1].ltekst
                            --Space after sentences is often missing. Add a space to puntcation.
                            ltekst = string.gsub( ltekst, "(%.)", "%1 " )
                            ltekst = string.gsub( ltekst, "(%?)", "%1 " )
                            ltekst = string.gsub( ltekst, "etails", "etails " )
                            ltekst = string.gsub( ltekst, "etails :", "etails: " )
                            ltekst = string.gsub( ltekst, "etails ", "etails: " )
                            ltekst = string.gsub( ltekst, "  ", " " )
                            ltekst = _h.trimLRspaces( ltekst )
                            --Replace double spaces by one space.
                            --ltekst = string.gsub( ltekst, "%s%s", "%s" )
                            dz.log( 'Update extensive Text: "' .. ltekst .. '"', dz.LOG_DEBUG )
                            dz.devices(wl_txt_idx).updateText( '<span style="line-height:' .. lineHeight .. '">' .. ltekst  .. '</span>' )
                            
                            if wl_hideNowarning == 1 and ltekst == 'Er zijn momenteel geen waarschuwingen van kracht.' then
                                -- set hidden.
                                if string.sub( dz.devices(wl_txt_idx).name, 1, 1) ~= '$' then
                                    dz.log( 'Set ' .. dz.devices(wl_txt_idx).name .. ' hidden!', dz.LOG_DEBUG )
                                    dz.devices(wl_txt_idx).rename( '$' .. dz.devices(wl_txt_idx).name )
                                end
                            else
                                -- set visiblle
                                if string.sub( dz.devices(wl_txt_idx).name, 1, 1) == '$' then
                                    dz.log( 'Set ' .. dz.devices(wl_txt_idx).name .. ' visible!', dz.LOG_DEBUG )
                                    dz.devices(wl_txt_idx).rename( string.gsub( dz.devices(wl_txt_idx).name, '%$', '') )
                                end
                            end
                        end
                        
                        -- Weather alert
                        if wl_alarm_idx ~= 99999 then
                            local alarm = liveweer_table[1].alarm
                            local lkop = liveweer_table[1].lkop
                            local wrschklr = liveweer_table[1].wrschklr
                            local wrsch_g = liveweer_table[1].wrsch_g
                            local wrsch_gts = liveweer_table[1].wrsch_gts
                            local wrsch_gc = liveweer_table[1].wrsch_gc
                            local alertText = lkop

                            --Set the alert color
                            if wrschklr == 'rood' then
                                alertLevel = dz.ALERTLEVEL_RED
                            elseif wrschklr == 'oranje' then
                                alertLevel = dz.ALERTLEVEL_ORANGE
                            elseif wrschklr == 'geel' then
                                alertLevel = dz.ALERTLEVEL_YELLOW
                            else
                                alertLevel = dz.ALERTLEVEL_GREY
                            end

                            if wrsch_g ~= '-'  then
                                -- There is a Warning
                                if wrsch_gc == 'rood' then
                                    htmlbgColor = cRed
                                elseif wrsch_gc == 'oranje' then
                                    htmlbgColor = cOrange
                                elseif wrsch_gc == 'geel' then
                                    htmlbgColor = cYellow
                                else
                                    htmlbgColor = cWhite
                                end
                                -- Add warning time and message on new line with the warning color.
                                alertText = alertText .. '\n<span style="line-height:' .. lineHeight .. ';  background-color: ' .. htmlbgColor .. '">Vanaf ' .. wrsch_g .. ' geldt code ' .. wrsch_gc .. '.</span>'                                
                            end
                        
                            -- Update the alert device with new alertLevel and alertText.
                            dz.log( 'Update Weeralarm: ' .. alertText, dz.LOG_DEBUG )
                            dz.devices(wl_alarm_idx).updateAlertSensor(alertLevel, alertText )
                            
                            if wl_hideNowarning == 1 and lkop == 'Er zijn geen waarschuwingen' and wrsch_g == '-' and wrsch_gc == '-' then
                                -- set hidden.
                                if string.sub( dz.devices(wl_alarm_idx).name, 1, 1) ~= '$' then
                                    dz.log( 'Set ' .. dz.devices(wl_alarm_idx).name .. ' hidden!', dz.LOG_DEBUG )
                                    dz.devices(wl_alarm_idx).rename( '$' .. dz.devices(wl_alarm_idx).name )
                                end
                            else
                                -- set visiblle
                                if string.sub( dz.devices(wl_alarm_idx).name, 1, 1) == '$' then
                                    dz.log( 'Set ' .. dz.devices(wl_alarm_idx).name .. ' visible!', dz.LOG_DEBUG )
                                    dz.devices(wl_alarm_idx).rename( string.gsub( dz.devices(wl_alarm_idx).name, '%$', '') )
                                end
                            end                            
                        end

                        -- Summary and forecast used for wl_verw_idx
                        samenv = liveweer_table[1].samenv
                        verw =  liveweer_table[1].verw

                    else
                        dz.log( 'No liveweer_table found', dz.LOG_ERROR )
                    end

                    ---- WK_VERW ----------------------------------
                    local wk_verw_table = result_table['wk_verw']
                    if type(wk_verw_table) == "table" then
                        dz.log( 'wk_verw_table: type = ' .. type(wk_verw_table), dz.LOG_DEBUG )
                        -- The data we get is for 5 days, including today:
                        --"dag": "12-05-2024",
                        --"image": "halfbewolkt",
                        --"max_temp": 18,
                        --"min_temp": 17,
                        --"windbft": 3,
                        --"windkmh": 18,
                        --"windknp": 10,
                        --"windms": 5,
                        --"windrgr": 121,
                        --"windr": "ZO",
                        --"neersl_perc_dag": 50,
                        --"zond_perc_dag": 68

                        d1date = string.sub( wk_verw_table[1].dag, 1, 5 )   -- We use only 'dd-mm'
                        d1nslperc = wk_verw_table[1].neersl_perc_dag
                        d1znperc = wk_verw_table[1].zond_perc_dag
                        d1mintemp = wk_verw_table[1].min_temp
                        d1maxtemp = wk_verw_table[1].max_temp
                            
                        d2date = string.sub( wk_verw_table[2].dag, 1, 5 )   -- We use only 'dd-mm'
                        d2nslperc = wk_verw_table[2].neersl_perc_dag 
                        d2znperc = wk_verw_table[2].zond_perc_dag
                        d2mintemp = wk_verw_table[2].min_temp
                        d2maxtemp = wk_verw_table[2].max_temp

                        
                        if wl_mintoD_idx ~= 99999 then
                            dz.log( 'Update Min. Temp toDay: ' .. d1mintemp, dz.LOG_DEBUG )
                            dz.devices(wl_mintoD_idx).updateTemperature( d1mintemp )
                        end
                        if wl_maxtoD_idx ~= 99999 then
                            dz.log( 'Update Max. Temp toDay: ' .. d1maxtemp, dz.LOG_DEBUG )
                            dz.devices(wl_maxtoD_idx).updateTemperature( d1maxtemp )
                        end
                        if wl_mintoM_idx ~= 99999 then
                            dz.log( 'Update Min. Temp toMorrow: ' .. d2mintemp, dz.LOG_DEBUG )
                            dz.devices(wl_mintoM_idx).updateTemperature( d2mintemp )
                        end
                        if wl_maxtoM_idx ~= 99999 then
                            dz.log( 'Update Max. Temp toMorrow: ' .. d2maxtemp, dz.LOG_DEBUG )
                            dz.devices(wl_maxtoM_idx).updateTemperature( d2maxtemp )
                        end

                        if wl_verw_idx ~= 99999 then
                            -- To calculate the expected sun hours.
                            local sunrise = dz.time.sunriseInMinutes
                            local sunset = dz.time.sunsetInMinutes
                            
                            -- For today -------------------------------------
                            local suntimeMinutes = ( d1znperc / 100 ) * ( sunset - sunrise ) 
                            local sunHours = suntimeMinutes / 60
                            local integerPart, fractionalPart = math.modf( sunHours )
                            fractionalPart = _u.round( fractionalPart * 60, 0 )
                            local sunMinutes = string.format( "%02d", fractionalPart )
                            local sunTime = integerPart .. ':' .. sunMinutes .. ' uur'
                            dz.log( 'Calculated sun time today = ' .. sunTime, dz.LOG_DEBUG )

                            local texta = ''
                            if wl_mintoD_idx ~= 99999 and wl_maxtoD_idx ~= 99999 then
                                texta = d1date .. ': Verwachte uren zon ' .. sunTime
                            else
                                -- arrowup = &#9650; arrowdown = &#9660; Degree celsius = &#8451;
                                texta = d1date .. ': <span style="color: ' ..  cGrey .. '">&#9660;</span>Temp. ' .. d1mintemp .. ' &#8451; - <span style="color: ' ..  cGrey .. '">&#9650;</span>Temp. ' .. d1maxtemp .. ' &#8451; - Zon ' .. sunTime
                            end
                            dz.log( texta, dz.LOG_DEBUG )

                            -- For tomorrow ----------------------------------
                            local suntimeMinutes = ( d2znperc / 100 ) * ( sunset - sunrise ) 
                            local sunHours = suntimeMinutes / 60
                            local integerPart, fractionalPart = math.modf( sunHours )
                            fractionalPart = _u.round( fractionalPart * 60, 0 )
                            local sunMinutes = string.format( "%02d", fractionalPart )
                            local sunTime = integerPart .. ':' .. sunMinutes .. ' uur'
                            dz.log( 'Calculated sun time tomorrow = ' .. sunTime, dz.LOG_DEBUG )

                            local textb = ''
                            if wl_mintoM_idx ~= 99999 and wl_maxtoM_idx ~= 99999 then
                                textb = d2date .. ': Verwachte uren zon ' .. sunTime
                            else
                                -- arrowup = &#9650; arrowdown = &#9660; Degree celsius = &#8451;
                                textb = d2date .. ': <span style="color: ' ..  cGrey .. '">&#9660;</span>Temp. ' .. d2mintemp .. ' &#8451; - <span style="color: ' ..  cGrey .. '">&#9650;</span>Temp. ' .. d2maxtemp .. ' &#8451; - Zon ' .. sunTime
                            end
                            dz.log( textb, dz.LOG_DEBUG )

                            -- Summary and forcast text.
                            dz.log( 'Samenvatting: ' .. samenv, dz.LOG_DEBUG ) 
                            dz.log( 'Verwachting: ' .. verw, dz.LOG_DEBUG )
                            local verw_text = ''
                            if wl_uselocationurl ~= 1 then
                                verw_text = '<span style="color: ' ..  htmlColor .. '">' .. samenv .. '\n' .. verw .. '</span>\n' .. texta .. '\n' .. textb
                            else
                                verw_text = '<a href="' .. wl_locationurl .. '" target="_blank"><span style="color: ' ..  htmlColor .. '">'  .. samenv .. '</span></a>\n<span style="color: ' ..  htmlColor .. '">' .. verw .. '</span>\n' .. texta .. '\n' .. textb
                            end                                
                            -- Update with new Text.
                            dz.log( 'Update Verwachting: ' .. verw_text, dz.LOG_DEBUG )
                            dz.devices(wl_verw_idx).updateText( verw_text )

                            if wl_icons_installed == 1 then
                                -- Update icon for tomorrow forecast.
                                local image = wk_verw_table[2].image
                                local iconNumber = getIconNumber( image )
                                dz.devices(wl_verw_idx).setIcon(iconNumber)
                            end
                        end                        
                    else
                        dz.log( 'No wk_verw_table found', dz.LOG_ERROR )
                    end

                    ---- UUR_VERW ----------------------------------
                    local uur_verw_table = result_table['uur_verw']
                    if type(uur_verw_table) == "table" then
                        dz.log( 'uur_verw_table: type = ' .. type(uur_verw_table), dz.LOG_DEBUG )
                        -- The data we get is for 24 hr, starting from next whole hour:
                        --"uur": "13-05-2024 21:00",
                        --"timestamp": 1715626800,
                        --"image": "wolkennacht",
                        --"temp": 20,
                        --"windbft": 2,
                        --"windkmh": 7,
                        --"windknp": 4,
                        --"windms": 2,
                        --"windrgr": 130,
                        --"windr": "ZO",
                        --"neersl": 0,
                        --"gr": 6
                       
                        if wl_rain_idx ~= 99999 then 
                            -- Loop through the table. Search for the first rain occurence.
                            local tc = #uur_verw_table
                            for i = 1, tc do                    
                                -- if found: Rain expexted at 'uur'
                                -- else: No rain expected until (last time found.)
                                local neersl = uur_verw_table[i].neersl
                                local hr = uur_verw_table[i].uur
                                if neersl ~= 0 then
                                    countRain = countRain + neersl
                                    local unixTime = dz.time.dateToTimestamp( hr, 'dd-mm-yyyy hh:MM' )
                                    if firstRain == 'norain' then firstRain = dz.time.timestampToDate( unixTime, 'hh:MM' , 0 ) end
                                    lastRain = dz.time.timestampToDate( unixTime, 'hh:MM' , 3600 ) -- is the next hour. 
                                else
                                    if firstRain ~= 'norain' then
                                        -- neersl == 0 after first episode of rain, so exit the loop.
                                        break
                                    end
                                end
                            end
                            if firstRain == 'norain' then
                                rainText = 'Geen neerslag verwacht < 24 uur'
                                dz.log( rainText, dz.LOG_DEBUG )
                            else
                                rainText = '<span style="color: ' ..  htmlColor .. '">1<sup>e</sup> Neerslag (totaal ' .. countRain .. ' l/m<sup>2</sup>) tussen ' .. firstRain .. ' - ' .. lastRain .. ' uur</span>'
                                dz.log( rainText, dz.LOG_DEBUG )
                            end
                            local rainTexta = d1date .. ': Kans op neerslag ' .. d1nslperc .. '%.'
                            dz.log( rainTexta, dz.LOG_DEBUG )
                            local rainTextb = d2date .. ': Kans op neerslag ' .. d2nslperc .. '%.'
                            dz.log( rainTextb, dz.LOG_DEBUG )

                             -- Update with new Text.
                            dz.log( 'Update Regenverwachting: ' .. rainText .. '\n' .. rainTexta .. '\n' .. rainTextb, dz.LOG_DEBUG )
                            dz.devices(wl_rain_idx).updateText( rainText .. '\n' .. rainTexta .. '\n' .. rainTextb )

                            if wl_icons_installed == 1 then
                                if rainText == 'Geen neerslag verwacht < 24 uur' then
                                    local iconNumber = getIconNumber( 'geenregen')
                                    dz.devices(wl_rain_idx).setIcon(iconNumber)
                                else
                                    -- Get rain icon
                                    local iconNumber = getIconNumber( 'regen' ) 
                                    dz.devices(wl_rain_idx).setIcon(iconNumber)
                                end
                            end
                        end
                    else
                        dz.log( 'No uur_verw_table found', dz.LOG_ERROR )
                    end
                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
}
-- 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.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest