local wibox = require('wibox') local awful = require('awful') local gears = require('gears') local naughty = require('naughty') local beautiful = require('beautiful') local dpi = beautiful.xresources.apply_dpi local config_dir = gears.filesystem.get_configuration_dir() local widget_icon_dir = config_dir .. 'widget/weather/icons/' local clickable_container = require('widget.clickable-container') local json = require('library.json') local config = require('configuration.config') local secrets = { key = config.widget.weather.key, city_id = config.widget.weather.city_id, units = config.widget.weather.units, update_interval = config.widget.weather.update_interval } local weather_icon_widget = wibox.widget { { id = 'icon', image = widget_icon_dir .. 'weather-error.svg', resize = true, forced_height = dpi(45), forced_width = dpi(45), widget = wibox.widget.imagebox, }, layout = wibox.layout.fixed.horizontal } local sunrise_icon_widget = wibox.widget { { id = 'sunrise_icon', image = widget_icon_dir .. 'sunrise.svg', resize = true, forced_height = dpi(18), forced_width = dpi(18), widget = wibox.widget.imagebox, }, layout = wibox.layout.fixed.horizontal } local sunset_icon_widget = wibox.widget { { id = 'sunset_icon', image = widget_icon_dir .. 'sunset.svg', resize = true, forced_height = dpi(18), forced_width = dpi(18), widget = wibox.widget.imagebox, }, layout = wibox.layout.fixed.horizontal } local refresh_icon_widget = wibox.widget { { id = 'refresh_icon', image = widget_icon_dir .. 'refresh.svg', resize = true, forced_height = dpi(18), forced_width = dpi(18), widget = wibox.widget.imagebox, }, layout = wibox.layout.fixed.horizontal } local refresh_button = clickable_container(refresh_icon_widget) refresh_button:buttons( gears.table.join( awful.button( {}, 1, nil, function() awesome.emit_signal('widget::weather_fetch') awesome.emit_signal('widget::forecast_fetch') end ) ) ) local refresh_widget = wibox.widget { refresh_button, bg = beautiful.transparent, shape = gears.shape.circle, widget = wibox.container.background } local weather_desc_temp = wibox.widget { { id = 'description', markup = 'Dust and clouds, -1000°C', font = 'Inter Regular 10', align = 'left', valign = 'center', widget = wibox.widget.textbox }, id = 'scroll_container', max_size = 345, speed = 75, expand = true, direction = 'h', step_function = wibox.container.scroll .step_functions.waiting_nonlinear_back_and_forth, fps = 30, layout = wibox.container.scroll.horizontal, } local weather_location = wibox.widget { { id = 'location', markup = 'Earth, Milky Way', font = 'Inter Regular 10', align = 'left', valign = 'center', widget = wibox.widget.textbox }, id = 'scroll_container', max_size = 345, speed = 75, expand = true, direction = 'h', step_function = wibox.container.scroll .step_functions.waiting_nonlinear_back_and_forth, fps = 30, layout = wibox.container.scroll.horizontal, } local weather_sunrise = wibox.widget { markup = '00:00', font = 'Inter Regular 10', align = 'center', valign = 'center', widget = wibox.widget.textbox } local weather_sunset = wibox.widget { markup = '00:00', font = 'Inter Regular 10', align = 'center', valign = 'center', widget = wibox.widget.textbox } local weather_data_time = wibox.widget { markup = '00:00', font = 'Inter Regular 10', align = 'center', valign = 'center', widget = wibox.widget.textbox } local weather_forecast_tooltip = awful.tooltip { text = 'Loading...', objects = {weather_icon_widget}, mode = 'outside', align = 'right', preferred_positions = {'left', 'right', 'top', 'bottom'}, margin_leftright = dpi(8), margin_topbottom = dpi(8) } local weather_report = wibox.widget { { { layout = wibox.layout.fixed.horizontal, spacing = dpi(10), { layout = wibox.layout.align.vertical, expand = 'none', nil, weather_icon_widget, nil }, { layout = wibox.layout.align.vertical, expand = 'none', nil, { layout = wibox.layout.fixed.vertical, weather_location, weather_desc_temp, { layout = wibox.layout.fixed.horizontal, spacing = dpi(7), { layout = wibox.layout.fixed.horizontal, spacing = dpi(3), sunrise_icon_widget, weather_sunrise }, { layout = wibox.layout.fixed.horizontal, spacing = dpi(3), sunset_icon_widget, weather_sunset }, { layout = wibox.layout.fixed.horizontal, spacing = dpi(3), refresh_widget, weather_data_time } } }, nil } }, margins = dpi(10), widget = wibox.container.margin }, forced_height = dpi(92), bg = beautiful.groups_bg, shape = function(cr, width, height) gears.shape.partially_rounded_rect(cr, width, height, true, true, true, true, beautiful.groups_radius) end, widget = wibox.container.background } -- Return weather symbol local get_weather_symbol = function() local symbol_tbl = { ['metric'] = '°C', ['imperial'] = '°F' } return symbol_tbl[secrets.units] end -- Create openweathermap script based on pass mode -- Mode must be `forecast` or `weather` local create_weather_script = function(mode) local weather_script = [[ KEY="]] .. secrets.key .. [[" CITY="]] .. secrets.city_id .. [[" UNITS="]] .. secrets.units .. [[" weather=$(curl -sf "http://api.openweathermap.org/data/2.5/]] .. mode .. [[?APPID="${KEY}"&id="${CITY}"&units="${UNITS}"") if [ ! -z "$weather" ]; then printf "${weather}" else printf "error" fi ]] return weather_script end awesome.connect_signal( 'widget::forecast_fetch', function() awful.spawn.easy_async_with_shell( create_weather_script('forecast'), function(stdout) if stdout:match('error') then weather_forecast_tooltip:set_markup('Can\'t retrieve data!') else local forecast_data = json.parse(stdout) local forecast = '' for i = 8, 40, 8 do local day = os.date('%A @ %H:%M', forecast_data.list[i].dt) local temp = math.floor(forecast_data.list[i].main.temp + 0.5) local feels_like = math.floor(forecast_data.list[i].main.feels_like + 0.5) local weather = forecast_data.list[i].weather[1].description -- Capitalize weather description weather = weather:sub(1, 1):upper() .. weather:sub(2) forecast = forecast .. '' .. day .. '\n' .. 'Weather: ' .. weather .. '\n' .. 'Temperature: ' .. temp .. get_weather_symbol() .. '\n' .. 'Feels like: ' .. feels_like .. get_weather_symbol() .. '\n\n' weather_forecast_tooltip:set_markup(forecast:sub(1, -2)) end end end ) end ) awesome.connect_signal( 'widget::weather_fetch', function() awful.spawn.easy_async_with_shell( create_weather_script('weather'), function(stdout) if stdout:match('error') then awesome.emit_signal( 'widget::weather_update', '...', 'Dust and clouds, -1000°C', 'Earth, Milky Way', '00:00', '00:00', '00:00' ) else -- Parse JSON string local weather_data = json.parse(stdout) -- Process weather data local location = weather_data.name local country = weather_data.sys.country local sunrise = os.date('%H:%M', weather_data.sys.sunrise) local sunset = os.date('%H:%M', weather_data.sys.sunset) local refresh = os.date('%H:%M', weather_data.dt) local temperature = math.floor(weather_data.main.temp + 0.5) local weather = weather_data.weather[1].description local weather_icon = weather_data.weather[1].icon -- Capitalize weather description local weather = weather:sub(1, 1):upper() .. weather:sub(2) -- Contantenate weather description and symbol local weather_description = weather .. ', ' .. temperature .. get_weather_symbol() -- Contantenate city and country local weather_location = location .. ', ' .. country awesome.emit_signal( 'widget::weather_update', weather_icon, weather_description, weather_location, sunrise, sunset, refresh ) end end ) end ) local update_widget_timer = gears.timer { timeout = secrets.update_interval, autostart = true, call_now = true, single_shot = false, callback = function() awesome.emit_signal('widget::weather_fetch') awesome.emit_signal('widget::forecast_fetch') end } awesome.connect_signal( 'system::network_connected', function() awesome.emit_signal('widget::weather_fetch') awesome.emit_signal('widget::forecast_fetch') end ) awesome.connect_signal( 'widget::weather_update', function(code, desc, location, sunrise, sunset, data_receive) local widget_icon_name = 'weather-error' local icon_tbl = { ['01d'] = 'sun_icon.svg', ['01n'] = 'moon_icon.svg', ['02d'] = 'dfew_clouds.svg', ['02n'] = 'nfew_clouds.svg', ['03d'] = 'dscattered_clouds.svg', ['03n'] = 'nscattered_clouds.svg', ['04d'] = 'dbroken_clouds.svg', ['04n'] = 'nbroken_clouds.svg', ['09d'] = 'dshower_rain.svg', ['09n'] = 'nshower_rain.svg', ['10d'] = 'd_rain.svg', ['10n'] = 'n_rain.svg', ['11d'] = 'dthunderstorm.svg', ['11n'] = 'nthunderstorm.svg', ['13d'] = 'snow.svg', ['13n'] = 'snow.svg', ['50d'] = 'dmist.svg', ['50n'] = 'nmist.svg', ['...'] = 'weather-error.svg' } widget_icon_name = icon_tbl[code] weather_icon_widget.icon:set_image(widget_icon_dir .. widget_icon_name) weather_icon_widget.icon:emit_signal('widget::redraw_needed') weather_desc_temp.description:set_markup(desc) weather_location.location:set_markup(location) weather_sunrise:set_markup(sunrise) weather_sunset:set_markup(sunset) weather_data_time:set_markup(data_receive) end ) return weather_report