* Found some additional instances of incorrect FSF addresses.
[notion/jeffpc.git] / contrib / scripts / weather.lua
blob72122943bc380a87417443bd1d0dbc51e2100309
1 -- Authors: Andrea Rossato <arossato@istitutocolli.org>
2 -- License: GPL, version 2 or later
3 -- Last Changed: 2006-07-07
4 --
5 -- weather.lua
7 --
8 -- NOTE: this script should be rewritten as a statusd script
9 --
11 -- weather.lua: an Ion3 applet for displaying weather information
13 -- INTRODUCTION
14 -- I discovered Ion last week, after reading an article on linux.com. I
15 -- installed it and, in a matter of a few hours, I decided to stick to
16 -- it. How can I say: I've been dreaming of a WM to be run from the
17 -- command line... and this is what Ion3 can do. And what a kind of a
18 -- command line! A Lua interpreter! I must confess I've never heard about
19 -- Lua before, and it was quite a surprise (I had the same feeling with
20 -- JavaScript some time ago).
21 -- I decided to write this applet mostly to learn and explore Lua,
22 -- especially for object-oriented programming.
24 -- ABOUT
25 -- This applet can be used to monitor the weather condition(s) of one or
26 -- more weather observation station(s). Data will be retrieved from
27 -- http://weather.noaa.gov and displayed in the statusbar.
29 -- USAGE
30 -- You need to dopath() this script (e.g. from cfg_ion3.lua):
31 -- dopath("weather")
33 -- - To monitor one station you can insert, in your cfg_statusbar.lua, within
34 -- mod_statusbar.launch_statusd{}, something like:
35 -- weather = {
36 -- station = "KNYC",
37 -- }
38 -- In your template insert something like:
39 -- %weather_location: %weather_tempC %weather_humidity (%weather_time)
41 -- Here's the list of all available data:
42 -- %weather_location
43 -- %weather_country
44 -- %weather_date
45 -- %weather_time (time of the latest report: your local time)
46 -- %weather_tempF (Fahrenheit)
47 -- %weather_tempC (Celsius)
48 -- %weather_dewpointF (Fahrenheit)
49 -- %weather_dewpointC (Celsius)
50 -- %weather_humidity
51 -- %weather_pressure (hPa)
52 -- %weather_wind
53 -- %weather_windspeed (MPH)
54 -- %weather_sky
55 -- %weather_weather
57 -- - If you want to monitor more stations you need to create a monitor
58 -- object for each one with the new_wm() function. After
59 -- dopath("weather") write something like:
60 -- mymonitor1 = new_wm("KNYC")
61 -- mymonitor2 = new_wm("LIBP")
63 -- You can create a new monitor also at run time. Get the Lua code prompt
64 -- (usually MOD1+F3) and run:
65 -- mymonitor3 = new_wm("LIBC")
67 -- Do not set any station in cfg_statusbar.lua, since that station would
68 -- be used by *all* monitors.
69 -- Each monitor will output the data either in %weater_meter_XXXX, where XXXX is
70 -- the station code (KNYC), and in %weather_meter.
72 -- CONFIGURATION
73 -- Default configuration values, that will apply to *all* monitors, can be
74 -- written in cfg_statusbar.lua in your ~/.ion3 directory.
76 -- Each monitor can be also configured at run-time.
77 -- For instance: if you are monitoring only one station, get the Lua code
78 -- prompt (usually MOD1+F3) and run:
79 -- WeatherMonitor.config.unit.tempC = "C"
80 -- or
81 -- WeatherMonitor.config.critical.tempC = "15"
82 -- You can save run-time configuration with the command:
83 -- WeatherMonitor.save_config()
84 -- This configuration will not overwrite the default station to be
85 -- monitored.
87 -- COMMANDS
88 -- Monitors are objects that export some public methods. You can use
89 -- these methods to change the state of the objects.
90 -- You can change configuration value and save them (see CONFIGURATION)
91 -- or issues some commands.
93 -- Suppose you have 2 monitors:
94 -- mon1 = new_wm("LIBP")
95 -- mon2 = new_wm("KNYC")
96 -- (Remember: in single mode the name of the object is WeatherMonitor.)
98 -- Get the Lua code prompt (usually MOD1+F3) and:
99 -- 1. to force one monitor to update the data:
100 -- mon1.update()
102 -- 2. to show the full report of the station:
103 -- mon2.show_data(_)
105 -- 3. to update data and show the report:
106 -- WeatherMonitor.update_and_show(_)
108 -- 4. to chnage station (only for run-time):
109 -- WeatherMonitor.set_station(_)
111 -- 5. to change one monitor configuration:
112 -- mon1.config.critical.tempC = "15"
114 -- Obviously you can create key-bindings to run these commands.
116 -- REVISIONS
117 -- 2006-07-07 first release
119 -- LEGAL
120 -- Copyright (C) 2006 Andrea Rossato
122 -- This program is free software; you can redistribute it and/or
123 -- modify it under the terms of the GNU General Public License
124 -- as published by the Free Software Foundation; either version 2
125 -- of the License, or (at your option) any later version.
127 -- This software is distributed in the hope that it will be useful,
128 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
129 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
130 -- GNU General Public License for more details.
132 -- You should have received a copy of the GNU General Public License
133 -- along with this program; if not, write to the Free Software
134 -- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
137 -- Have fun!
139 -- Andrea Rossato arossato AT istitutocolli DOT org
142 function new_wm(station)
143 local this = {
144 config = {
145 station = station,
146 interval =30 * 60 * 1000, -- check every 30 minutes
147 unit = {
148 tempC = "°",
149 tempF = "F",
150 humidity = "%",
151 pressure = "hPa",
152 windspeed = "MPH",
155 -- Threshold information. These values should likely be tweaked to
156 -- suit local conditions.
157 important = {
158 tempC = 10,
159 tempF = 50,
160 windspeed = 5,
161 humidity = 30,
163 critical = {
164 tempC = 26,
165 tempF = 78,
166 windspeed = 20,
167 humidity = 60,
170 status_timer = ioncore.create_timer(),
171 url = "http://weather.noaa.gov/pub/data/observations/metar/decoded/",
172 paths = ioncore.get_paths(),
173 data = {
174 timezone = tonumber(os.date("%z"))
178 -- process configuration:
179 -- - config in cfg_statusbar.lua will overwrite default config
180 -- - run-time configuration (to be saved at run-time with this.save_config())
181 -- will overwrite default config but will be overwtitten by cfg_statusbar.lua
182 this.process_config = function()
183 table.merge = function(t1, t2)
184 local t=table.copy(t1, false)
185 for k, v in pairs(t2) do
186 t[k]=v
188 return t
190 if not this.config.station then this.config.station = "LIPB" end
191 local config = ioncore.read_savefile("cfg_weather_"..this.config.station)
192 if config then
193 this.config = table.merge(this.config, config)
195 config = ioncore.read_savefile("cfg_statusd")
196 if config.weather then
197 this.config = table.merge(this.config, config.weather)
201 -- retrive data from server
202 this.update_data = function()
203 -- wget options
204 -- -b go into the background (otherwise it will hang ion startup
205 -- until data file is download)
206 -- -o output error log (specify filename if you want to save output)
207 local command = "wget -b -o /dev/null -O "..this.paths.sessiondir.."/"..this.config.station..".dat "..
208 this.url..this.config.station..".TXT"
209 os.execute(command)
210 local f = io.open(this.paths.sessiondir .."/".. this.config.station..".dat", "r")
211 if not f then return end
212 local s=f:read("*all")
213 f:close()
214 this.raw_data = s
215 os.execute("rm "..this.paths.sessiondir .."/".. this.config.station..".dat")
218 -- process retrived data and store them in this.data
219 this.process_data = function()
220 local s = this.raw_data
221 _, _, this.data.location, this.data.country =
222 string.find(s, "^(.+)%,%s(.+)%(%u+%)" )
223 _, _, this.data.date, this.data.time =
224 string.find(s, ".+%/%s([%d.]+)%s(%d+)%sUTC" )
225 _, _, this.data.wind, this.data.windspeed =
226 string.find(s, "Wind%:%s(.+%sat%s(%d+)%sMPH)" )
227 _, _, this.data.sky = string.find(s, "Sky%sconditions:%s(.-)%c" )
228 _, _, this.data.tempF, this.data.tempC =
229 string.find(s, "Temperature:%s([%d%.]+)%sF%s%(([%d%.]+)%sC%)%c" )
230 _, _, this.data.dewpointF, this.data.dewpointC =
231 string.find(s, "Dew%sPoint:%s([%d%.]+)%sF%s%(([%d%.]+)%sC%)" )
232 _, _, this.data.humidity =
233 string.find(s, "Relative%sHumidity:%s(%d+)%%")
234 _, _, this.data.pressure =
235 string.find(s, "Pressure%s.+%((.+)%shPa%)" )
236 _, _, this.data.weather =
237 string.find(s, "Weather:%s(.-)%c" )
238 this.format_time()
241 -- format teh time string to get hh:mm
242 this.format_time = function()
243 local time
244 if this.data.time then
245 time = tonumber(this.data.time) + tonumber(this.data.timezone)
246 else return
248 if time > 2400 then
249 time = tostring(time - 2400)
251 if string.match(time, "^%d%d$") then
252 time = "00"..time
254 if string.match(time, "^%d%d%d$") then
255 time = "0"..time
257 this.data.time = tostring(time):gsub("(%d%d)(%d%d)","%1%:%2")
260 -- get threshold information
261 this.get_hint = function(meter, val)
262 local hint = "normal"
263 local crit = this.config.critical[meter]
264 local imp = this.config.important[meter]
265 if crit and tonumber(val) > crit then
266 hint = "critical"
267 elseif imp and tonumber(val) > imp then
268 hint = "important"
270 return hint
273 -- get the unit of each meter
274 this.get_unit = function(meter)
275 local unit = this.config.unit[meter]
276 if unit then return unit end
277 return ""
280 -- update information for mod_statusbar
281 this.notify = function()
282 for i,v in pairs(this.data) do
283 mod_statusbar.inform("weather_"..i.."_"..this.config.station.."_hint", this.get_hint(i, v))
284 mod_statusbar.inform("weather_"..i.."_hint", this.get_hint(i, v))
285 if not v then v = "N/A" end
286 mod_statusbar.inform("weather_"..i.."_"..this.config.station, v..this.get_unit(i))
287 mod_statusbar.inform("weather_"..i, v..this.get_unit(i))
289 mod_statusbar.update()
292 -- some public methods
294 -- save object state (each monitor will store its configuration in
295 -- a file named cfg_weather_XXXX where XXXX is the station code
296 -- this configuration will apply only to the monitor watching that
297 -- station. In other words, you cannot set the default station for
298 -- the monitor with this.set_station() (see comments below).
299 this.save_config = function()
300 ioncore.write_savefile("cfg_weather_"..this.config.station, this.config)
303 -- restarts the object
304 this.update = function()
305 this.init()
308 -- shows full report
309 this.show_data = function(mplex)
310 mod_query.message(mplex, this.raw_data)
313 -- updates data and shows updated full report
314 this.update_and_show = function(mplex)
315 this.init()
316 this.show_data(mplex)
319 -- changes station. the new station will not be saved:
320 -- to change station edit the configuration or start the
321 -- monitor with the station as a paramenter, like:
322 -- mymon = new_wm("KNYC")
323 this.set_station = function(mplex)
324 local handler = function(mplex, str)
325 this.config.station = str;
326 this.init()
328 mod_query.query(mplex, TR("Enter a station code:"), nil, handler,
329 nil, "weather")
332 -- constructor
333 this.init = function()
334 this.update_data()
335 this.process_data()
336 if mod_statusbar ~= nil then
337 this.notify()
338 this.status_timer:set(this.config.interval, this.init)
342 -- initialize the object
343 this.process_config()
344 this.init()
345 return {
346 config = this.config,
347 data = this.data,
348 save_config = this.save_config,
349 update = this.update,
350 show_data = this.show_data,
351 update_and_show = this.update_and_show,
352 set_station = this.set_station,
356 -- start default monitor
357 WeatherMonitor = new_wm()