]> git.madduck.net Git - etc/awesome.git/blob - widget/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:

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