]> git.madduck.net Git - etc/awesome.git/blob - widgets/weather.lua

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

wiki updated; #186
[etc/awesome.git] / widgets / weather.lua
1
2 --[[
3                                                   
4      Licensed under GNU General Public License v2 
5       * (c) 2015, Luke Bonham                     
6                                                   
7 --]]
8
9 local newtimer     = require("lain.helpers").newtimer
10 local read_pipe    = require("lain.helpers").read_pipe
11
12 local async        = require("lain.asyncshell")
13 local json         = require("lain.util").dkjson
14 local lain_icons   = require("lain.helpers").icons_dir
15
16 local naughty      = require("naughty")
17 local wibox        = require("wibox")
18
19 local math         = { floor  = math.floor }
20 local mouse        = mouse
21 local string       = { format = string.format,
22                        gsub   = string.gsub }
23
24 local setmetatable = setmetatable
25
26 -- OpenWeatherMap
27 -- current weather and X-days forecast
28 -- lain.widgets.weather
29
30 local function worker(args)
31     local weather               = {}
32     local args                  = args or {}
33     local APPID                 = args.APPID or "3e321f9414eaedbfab34983bda77a66e" -- lain default
34     local timeout               = args.timeout or 900   -- 15 min
35     local timeout_forecast      = args.timeout or 86400 -- 24 hrs
36     local current_call          = "curl -s 'http://api.openweathermap.org/data/2.5/weather?id=%s&units=%s&lang=%s&APPID=%s'"
37     local forecast_call         = "curl -s 'http://api.openweathermap.org/data/2.5/forecast/daily?id=%s&units=%s&lang=%s&cnt=%s&APPID=%s'"
38     local city_id               = args.city_id or 0 -- placeholder
39     local units                 = args.units or "metric"
40     local lang                  = args.lang or "en"
41     local cnt                   = args.cnt or 7
42     local date_cmd              = args.date_cmd or "date -u -d @%d +'%%a %%d'"
43     local icons_path            = args.icons_path or lain_icons .. "openweathermap/"
44     local notification_preset   = args.notification_preset or {}
45     local notification_text_fun = args.notification_text_fun or
46                                   function (wn)
47                                       local day = string.gsub(read_pipe(string.format(date_cmd, wn["dt"])), "\n", "")
48                                       local tmin = math.floor(wn["temp"]["min"])
49                                       local tmax = math.floor(wn["temp"]["max"])
50                                       local desc = wn["weather"][1]["description"]
51
52                                       return string.format("<b>%s</b>: %s, %d - %d ", day, desc, tmin, tmax)
53                                   end
54     local weather_na_markup     = args.weather_na_markup or " N/A "
55     local followmouse           = args.followmouse or false
56     local settings              = args.settings or function() end
57
58     weather.widget    = wibox.widget.textbox(weather_na_markup)
59     weather.icon_path = icons_path .. "na.png"
60     weather.icon      = wibox.widget.imagebox(weather.icon_path)
61
62     function weather.show(t_out)
63         weather.hide()
64
65         if followmouse then
66             notification_preset.screen = mouse.screen
67         end
68
69         weather.notification = naughty.notify({
70             text    = weather.notification_text,
71             icon    = weather.icon_path,
72             timeout = t_out,
73             preset  = notification_preset
74         })
75     end
76
77     function weather.hide()
78         if weather.notification then
79             naughty.destroy(weather.notification)
80             weather.notification = nil
81         end
82     end
83
84     function weather.attach(obj)
85         obj:connect_signal("mouse::enter", function()
86             weather.show(0)
87         end)
88         obj:connect_signal("mouse::leave", function()
89             weather.hide()
90         end)
91     end
92
93     function weather.forecast_update()
94         local cmd = string.format(forecast_call, city_id, units, lang, cnt, APPID)
95         async.request(cmd, function(f)
96             local pos, err
97             weather_now, pos, err = json.decode(f, 1, nil)
98
99             if not err and weather_now and tonumber(weather_now["cod"]) == 200 then
100                 weather.notification_text = ''
101                 for i = 1, weather_now["cnt"] do
102                     weather.notification_text = weather.notification_text ..
103                                                 notification_text_fun(weather_now["list"][i])
104
105                     if i < weather_now["cnt"] then
106                         weather.notification_text = weather.notification_text .. "\n"
107                     end
108                 end
109             else
110                 weather.notification_text = "API/connection error or invalid city ID"
111             end
112         end)
113     end
114
115     function weather.update()
116         local cmd = string.format(current_call, city_id, units, lang, APPID)
117         async.request(cmd, function(f)
118             local pos, err
119             weather_now, pos, err = json.decode(f, 1, nil)
120
121             if not err and weather_now ~= nil and tonumber(weather_now["cod"]) == 200 then
122                 weather.icon_path = icons_path .. weather_now["weather"][1]["icon"] .. ".png"
123                 widget = weather.widget
124                 settings()
125             else
126                 weather.icon_path = icons_path .. "na.png"
127                 weather.widget:set_markup(weather_na_markup)
128             end
129
130             weather.icon:set_image(weather.icon_path)
131         end)
132     end
133
134     weather.attach(weather.widget)
135
136     newtimer("weather-" .. city_id, timeout, weather.update)
137     newtimer("weather_forecast-" .. city_id, timeout, weather.forecast_update, nil)
138
139     return setmetatable(weather, { __index = weather.widget })
140 end
141
142 return setmetatable({}, { __call = function(_, ...) return worker(...) end })