Fetch weather forecast from Yr.no on LogicMachine #
Scheduled script #
Make sure that your script does not fetch data too quickly (we recommend at most once in four hours).
Change latitude
, longitude
, group addresses and group names as needed.
local latitude = 45.921373 -- 'Mandello del Lario'
local longitude = 9.314482 -- 'Mandello del Lario'
function create(config)
local address = config.address
local object = grp.find(address)
if not object then
grp.create(config)
end
return address
end
local status_temp = create({ datatype = dt.float16, address = '33/1/1', name = 'current_temp' })
local status_pressure = create({ datatype = dt.uint16, address = '33/1/2', name = 'current_press' })
local status_humidity = create({ datatype = dt.scale, address = '33/1/3', name = 'current_humid' })
local status_wind_direction = create({ datatype = dt.float16, address = '33/1/4', name = 'current_wind_dir' })
local status_wind_speed = create({ datatype = dt.float16, address = '33/1/5', name = 'current_wind_sp' })
local status_uv_index = create({ datatype = dt.uint8, address = '33/1/6', name = 'current_uv_index' })
local status_feelslike_temp = create({ datatype = dt.float16, address = '33/1/7', name = 'current_feels_temp' })
local status_rain_today = create({ datatype = dt.bool, address = '33/1/8', name = 'rain_today' })
local status_snow_today = create({ datatype = dt.bool, address = '33/1/9', name = 'snow_today' })
local status_min_temp_today = create({ datatype = dt.float16, address = '33/1/10', name = 'today_temp_min' })
local status_max_temp_today = create({ datatype = dt.float16, address = '33/1/11', name = 'today_temp_max' })
local status_min_pressure_today = create({ datatype = dt.uint16, address = '33/1/12', name = 'today_press_min' })
local status_max_pressure_today = create({ datatype = dt.uint16, address = '33/1/13', name = 'today_press_max' })
local status_min_wind_speed_today = create({ datatype = dt.float16, address = '33/1/14', name = 'today_wind_min' })
local status_max_wind_speed_today = create({ datatype = dt.float16, address = '33/1/15', name = 'today_wind_max' })
local status_min_uv_today = create({ datatype = dt.uint8, address = '33/1/16', name = 'today_uv_min' })
local status_max_uv_today = create({ datatype = dt.uint8, address = '33/1/17', name = 'today_uv_max' })
local status_rain_tomor = create({ datatype = dt.bool, address = '33/1/18', name = 'rain_tomor' })
local status_snow_tomor = create({ datatype = dt.bool, address = '33/1/19', name = 'snow_tomor' })
local status_min_temp_tomor = create({ datatype = dt.float16, address = '33/1/20', name = 'tomor_temp_min' })
local status_max_temp_tomor = create({ datatype = dt.float16, address = '33/1/21', name = 'tomor_temp_max' })
local status_min_pressure_tomor = create({ datatype = dt.uint16, address = '33/1/22', name = 'tomor_press_min' })
local status_max_pressure_tomor = create({ datatype = dt.uint16, address = '33/1/23', name = 'tomor_press_max' })
local status_min_wind_speed_tomor = create({ datatype = dt.float16, address = '33/1/24', name = 'tomor_wind_min' })
local status_max_wind_speed_tomor = create({ datatype = dt.float16, address = '33/1/25', name = 'tomor_wind_max' })
local status_min_uv_tomor = create({ datatype = dt.uint8, address = '33/1/26', name = 'tomor_uv_min' })
local status_max_uv_tomor = create({ datatype = dt.uint8, address = '33/1/27', name = 'tomor_uv_max' })
-- mm for next 6 hours
local status_precipitation_amount_6h = create({ datatype = dt.float16, address = '33/1/28', name = 'precipitation_amount_6h' })
-- mm for current day + 3 previous hours
local status_precipitation_amount_today = create({ datatype = dt.float16, address = '33/1/29', name = 'precipitation_amount_today' })
local rain_tod_YR = false -- is rainy or not (for today)
local snow_tod_YR = false -- is snowy or not (for today)
local rain_tom_YR = false -- is rainy or not (for tomorrow)
local snow_tom_YR = false -- is snowy or not (for tomorrow)
-- current temperature, air pressure, humidity, wind direction and speed
local temperature_YR = -273 -- temperature in degree Celsius
local air_pressure_YR = -1 -- air pressure in hPa
local humidity_YR = -1 -- air humidity in %
local wind_direction_YR = -1 -- wind direction in degrees [0; 360)
local wind_speed_YR = -1 -- wind speed in m/s
local uv_index_YR = -1 -- UV index
local feelslike_temp_YR = -273 -- feels like temperature in degree Celsius
local precipitation_amount_6h = -1 -- participation amount in mm next 6 hours
local precipitation_amount_today = 0 -- participation amount in mm for current day + 3 previous hours
function init_min_max_table() -- initialize table for minimum, maximum temperature, pressure, windspeed
return {
min_t = 8000, -- minimum temperature for today
max_t = -273, -- maximum temperature for today
min_p = 8000, -- minimum pressure for today
max_p = -1, -- maximum pressure for today
min_w = 8000, -- minimum wind speed for today
max_w = -1, -- maximum wind speed for today
min_uv = 8000, -- minimum UV index
max_uv = -1, -- maximum UV index
}
end
function set_timestamp(day) -- if day = 0 today; if day = 1 tomorrow, if day = 2 day after tomorrow
local date = os.date('*t')
date.day = date.day + day
local time = os.time(date)
return os.date('%Y-%m-%d', time), date.hour -- timestamp and current hour
end
-- minimum maximum values of temperature, pressure and windspeed
function min_max_temp_press_wind(dat_tab, min_max_tab)
min_max_tab.min_t = math.min(min_max_tab.min_t, dat_tab.air_temperature or 8000)
min_max_tab.max_t = math.max(min_max_tab.max_t, dat_tab.air_temperature or -273)
min_max_tab.min_p = math.min(min_max_tab.min_p, dat_tab.air_pressure_at_sea_level or 8000)
min_max_tab.max_p = math.max(min_max_tab.max_p, dat_tab.air_pressure_at_sea_level or -1)
min_max_tab.min_w = math.min(min_max_tab.min_w, dat_tab.wind_speed or 8000)
min_max_tab.max_w = math.max(min_max_tab.max_w, dat_tab.wind_speed or -1)
min_max_tab.min_uv = math.min(min_max_tab.min_uv, dat_tab.ultraviolet_index_clear_sky or 8000)
min_max_tab.max_uv = math.max(min_max_tab.max_uv, dat_tab.ultraviolet_index_clear_sky or -1)
return min_max_tab
end
function check_for_rain(next1h)
local is_rainy = false
local code = ''
if type(next1h) == 'table' and type(next1h.summary) == 'table' then
code = next1h.summary.symbol_code or ''
end
if code:find('rain') then
is_rainy = true
end
return is_rainy
end
function check_for_snow(next1h)
local is_snowy = false
local code = ''
if type(next1h) == 'table' and type(next1h.summary) == 'table' then
code = next1h.summary.symbol_code or ''
end
if code:find('snow') then
is_snowy = true
end
return is_snowy
end
function calc_heat_index(temperature, humidity)
if temperature < 26.7 then
log('for heat_index calculation temperature should be higher than +26.7 Celsius degree')
return -273
end
if humidity < 0 or humidity > 100 then
log('humidity should be in a range from 0 ... 100%')
return -273
end
local T = temperature
local H = humidity
local c_1 = -8.785
local c_2 = 1.611
local c_3 = 2.339
local c_4 = -0.146
local c_5 = -0.0123
local c_6 = -0.0164
local c_7 = 0.00221
local c_8 = 0.000725
local c_9 = -0.00000358
local heat_index = c_1+c_2*T+c_3*H+c_4*T*H+c_5*T*T+c_6*H*H+c_7*T*T*H+c_8*T*H*H+c_9*T*T*H*H
return heat_index
end
function calc_wind_chill(temperature, wind_speed)
if temperature > 10 then
log('temperature should be less than +10.0 Celsius degree')
return -273
end
if wind_speed < 0 or wind_speed > 49.2 then
log('wind speed should be in a range 0 ... 49.2 m/s')
return -273
end
local T_air = temperature -- air temperature Celsius degree
local a = 0.62 -- coef
local b = 0.51 -- coef
local T_1 = 13.1 -- Celsius degree
local T_2 = 14.6 -- Celsius degree
local M_0 = 1.3333 -- m/s
local M = wind_speed -- m/s
local T_wind_chill
if M > M_0 then
T_wind_chill = (a*T_air + T_1) + (b*T_air - T_2)*(M/M_0)^0.16
else
T_wind_chill = T_air
end
return T_wind_chill
end
function calc_feelslike(temperature, wind_speed, humidity)
local T_feelslike
if temperature <= 10.0 then -- if less than 10.0 Celsius degree (or equal) then wind chill
T_feelslike = calc_wind_chill(temperature, wind_speed)
elseif temperature >= 26.7 then -- if more than 26.7 Celsius degree (or equal) then heat index
T_feelslike = calc_heat_index(temperature, humidity)
else -- other cases just return temperature
T_feelslike = temperature
end
return T_feelslike
end
local min_max_today = init_min_max_table() -- table for minimum maximum temperature, air pressure, wind direction
local min_max_tomor = init_min_max_table() -- table for minimum maximum temperature, air pressure, wind direction
https = require('ssl.https')
json = require('json')
ltn12 = require('ltn12')
args = 'lat=' .. latitude .. '&lon=' .. longitude
url = 'https://api.met.no/weatherapi/locationforecast/2.0/complete?' .. args
mac = 0
io.readfile('/sys/class/net/eth0/address'):gsub('%x%x', function(v)
mac = mac * 256 + tonumber(v, 16)
end)
response = {}
res, code = https.request({
url = url,
protocol = 'tlsv12',
headers = {
['user-agent'] = 'LM ' .. mac
},
sink = ltn12.sink.table(response)
})
if res and code == 200 then
data = json.pdecode(table.concat(response))
else
log('request error', res, code)
return
end
if type(data) ~= 'table' then
log('failed to decode data')
return
end
timestamp, hour = set_timestamp(0) -- current day
if hour < 10 then
timestamp_current_hour = timestamp .. 'T0' .. hour .. ':00:00Z' -- time stamp for current hour
else
timestamp_current_hour = timestamp .. 'T' .. hour .. ':00:00Z' -- time stamp for current hour
end
--log('timestamp = ' .. timestamp)
timestamp_next = set_timestamp(1) -- timestamp for tomorrow
--log('timestamp_next = ' .. timestamp_next)
--timestamp_after_next = set_timestamp(2) -- timestamp for day after tomorrow
--log('timestamp_after_next = ' .. timestamp_after_next)
--timestamp_after_after = set_timestamp(3) -- timestamp for day after day after tomorrow
--log('timestamp_after_after = ' .. timestamp_after_after)
for i, entry in ipairs(data.properties.timeseries) do
-- for weather now
if entry.time == timestamp_current_hour then -- get data only for one hour (current hour)
current_data = entry.data.instant.details
if type(current_data) == 'table' then
temperature_YR = current_data.air_temperature or -273 -- temperature in Celsius degree
air_pressure_YR = current_data.air_pressure_at_sea_level or -1 -- air pressure in hPa
humidity_YR = current_data.relative_humidity or -1 -- air humidity in %
wind_direction_YR = current_data.wind_from_direction or -1 -- wind direction in degrees [0;360)
wind_speed_YR = current_data.wind_speed or -1 -- wind speed in m/s
uv_index_YR = current_data.ultraviolet_index_clear_sky or -1 -- uv index
feelslike_temp_YR = calc_feelslike(temperature_YR, wind_speed_YR, humidity_YR) -- feels like temperature in Celsius degree
end
next_6h = entry.data.next_6_hours.details
if type(next_6h) == 'table' then
precipitation_amount_6h = next_6h.precipitation_amount or -1 -- precipitation amount in mm, next 6 hours
end
end
if entry.time:sub(1, #timestamp) == timestamp then -- checks remaining hours + 3 previous hours of current day
current_data = entry.data.instant.details
if type(current_data) == 'table' then
min_max_today = min_max_temp_press_wind(current_data, min_max_today)
end
if rain_tod_YR == false then -- if detected rain does not check for rain
rain_tod_YR = check_for_rain(entry.data.next_1_hours)
end
if snow_tod_YR == false then -- if detected snow does not check for snow
snow_tod_YR = check_for_snow(entry.data.next_1_hours)
end
precipitation_amount_today = precipitation_amount_today + (entry.data.next_1_hours.details.precipitation_amount or 0)
end
if entry.time:sub(1, #timestamp_next) == timestamp_next then -- checks 24 hours of next day
tomor_data = entry.data.instant.details
if type(tomor_data) == 'table' then
min_max_tomor = min_max_temp_press_wind(tomor_data, min_max_tomor)
end
if rain_tom_YR == false then -- if detected rain does not check for rain
rain_tom_YR = check_for_rain(entry.data.next_1_hours)
end
if snow_tom_YR == false then -- if detected snow does not check for snow
snow_tom_YR = check_for_snow(entry.data.next_1_hours)
end
end
end
-- data for current hour
grp.checkwrite(status_temp, temperature_YR)
grp.checkwrite(status_pressure, air_pressure_YR)
grp.checkwrite(status_humidity, humidity_YR)
grp.checkwrite(status_wind_direction, wind_direction_YR)
grp.checkwrite(status_wind_speed, wind_speed_YR)
grp.checkwrite(status_uv_index, uv_index_YR)
grp.checkwrite(status_feelslike_temp, feelslike_temp_YR)
-- precipitation mm for next 6 hours
grp.checkwrite(status_precipitation_amount_6h, precipitation_amount_6h)
-- precipitation mm for today (remaining day + 3 previous hours)
grp.checkwrite(status_precipitation_amount_today, precipitation_amount_today)
-- data for today: rain, snow; min and max for temperature, pressure, wind
grp.checkwrite(status_rain_today, rain_tod_YR)
grp.checkwrite(status_snow_today, snow_tod_YR)
grp.checkwrite(status_min_temp_today, min_max_today.min_t)
grp.checkwrite(status_max_temp_today, min_max_today.max_t)
grp.checkwrite(status_min_pressure_today, min_max_today.min_p)
grp.checkwrite(status_max_pressure_today, min_max_today.max_p)
grp.checkwrite(status_min_wind_speed_today, min_max_today.min_w)
grp.checkwrite(status_max_wind_speed_today, min_max_today.max_w)
grp.checkwrite(status_min_uv_today, min_max_today.min_uv)
grp.checkwrite(status_max_uv_today, min_max_today.max_uv)
-- data for tomorrow: rain, snow; min and max for temperature, pressure, wind
grp.checkwrite(status_rain_tomor, rain_tom_YR)
grp.checkwrite(status_snow_tomor, snow_tom_YR)
grp.checkwrite(status_min_temp_tomor, min_max_tomor.min_t)
grp.checkwrite(status_max_temp_tomor, min_max_tomor.max_t)
grp.checkwrite(status_min_pressure_tomor, min_max_tomor.min_p)
grp.checkwrite(status_max_pressure_tomor, min_max_tomor.max_p)
grp.checkwrite(status_min_wind_speed_tomor, min_max_tomor.min_w)
grp.checkwrite(status_max_wind_speed_tomor, min_max_tomor.max_w)
grp.checkwrite(status_min_uv_tomor, min_max_tomor.min_uv)
grp.checkwrite(status_max_uv_tomor, min_max_tomor.max_uv)