1 -- Authors: Andrea Rossato <arossato@istitutocolli.org>
2 -- License: GPL, version 2 or later
3 -- Last Changed: 2006-07-07
8 -- NOTE: this script should be rewritten as a statusd script
11 -- weather.lua: an Ion3 applet for displaying weather information
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.
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.
30 -- You need to dopath() this script (e.g. from cfg_ion3.lua):
33 -- - To monitor one station you can insert, in your cfg_statusbar.lua, within
34 -- mod_statusbar.launch_statusd{}, something like:
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:
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)
51 -- %weather_pressure (hPa)
53 -- %weather_windspeed (MPH)
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.
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"
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
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:
102 -- 2. to show the full report of the station:
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.
117 -- 2006-07-07 first release
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
139 -- Andrea Rossato arossato AT istitutocolli DOT org
142 function new_wm(station
)
146 interval
=30 * 60 * 1000, -- check every 30 minutes
155 -- Threshold information. These values should likely be tweaked to
156 -- suit local conditions.
170 status_timer
= ioncore
.create_timer(),
171 url
= "http://weather.noaa.gov/pub/data/observations/metar/decoded/",
172 paths
= ioncore
.get_paths(),
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
190 if not this
.config
.station
then this
.config
.station
= "LIPB" end
191 local config
= ioncore
.read_savefile("cfg_weather_"..this
.config
.station
)
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()
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"
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")
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" )
241 -- format teh time string to get hh:mm
242 this
.format_time
= function()
244 if this
.data
.time
then
245 time
= tonumber(this
.data
.time
) + tonumber(this
.data
.timezone
)
249 time
= tostring(time
- 2400)
251 if string.match(time
, "^%d%d$") then
254 if string.match(time
, "^%d%d%d$") then
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
267 elseif imp
and tonumber(val
) > imp
then
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
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()
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
)
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
;
328 mod_query
.query(mplex
, TR("Enter a station code:"), nil, handler
,
333 this
.init
= function()
336 if mod_statusbar
~= nil then
338 this
.status_timer
:set(this
.config
.interval
, this
.init
)
342 -- initialize the object
343 this
.process_config()
346 config
= this
.config
,
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()