Yr.no weather forecast

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).

Weather_forecast scheduled

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)