aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/weather.py
diff options
context:
space:
mode:
authorMattias Andrée <maandree@operamail.com>2014-03-02 20:29:01 +0100
committerMattias Andrée <maandree@operamail.com>2014-03-02 20:29:01 +0100
commitdcca5909918b267a473d6f85be8d5be630495f89 (patch)
tree3ef55412f4f738614f580a3f10d3ed83e0204ad3 /src/plugins/weather.py
parentm + fix threading weirdness + add clock example (diff)
downloadxpybar-dcca5909918b267a473d6f85be8d5be630495f89.tar.gz
xpybar-dcca5909918b267a473d6f85be8d5be630495f89.tar.bz2
xpybar-dcca5909918b267a473d6f85be8d5be630495f89.tar.xz
partial implemention of weather monitoring
Signed-off-by: Mattias Andrée <maandree@operamail.com>
Diffstat (limited to 'src/plugins/weather.py')
-rw-r--r--src/plugins/weather.py141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/plugins/weather.py b/src/plugins/weather.py
new file mode 100644
index 0000000..58d12ad
--- /dev/null
+++ b/src/plugins/weather.py
@@ -0,0 +1,141 @@
+# -*- python -*-
+'''
+xpybar – xmobar replacement written in python
+Copyright © 2014 Mattias Andrée (maandree@member.fsf.org)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+'''
+
+from util import *
+
+
+class Weather:
+ '''
+ Weather monitor
+
+ @variable icao:str The station's International Civil Aviation Organization airport code
+ @variable station:str The station as a human readable, not necessarily only the airport name
+ @variable location:str Human readable location, country or state (abbreviated) and country.
+ @variable latitude:float The latitude position of the station, in degrees to north with two decimals
+ @variable longitude:float The longitude position of the station, in degrees to east with two decimals
+ @variable headers:list<str> Headers in the decoded metar data
+ @variable fields:dict<str, str> Fields in the decoded metar data
+ @variable time:(int, int, int) The time observation was made: day of month, hour and minute, in UTC
+
+ The following will be `None` if not found in the data, but it is probably found if it should not be `None`.
+ They can be floating point, but are most often integers.
+
+ @variable wind_dir:float? The wind direction, `None` if variable
+ @variable wind_speed:float The wind speed in knots
+ @variable wind_gusts:float? The wind gusts in knots
+ @variable wind_var:(float, float)? The wind direction range, `None` if less than 60° variation
+ @variable temp:float The temperature in °C
+ @variable dew:float The dew point in °C
+ @variable pressure:flaot The pressure in hPa
+ @variable visibility:float The visibility in statute miles
+ '''
+
+
+ def __init__(self, station):
+ '''
+ Constructor
+
+ @param station:str The station's ICAO code (International Civil Aviation Organization airport code)
+ '''
+ self.icao = station
+ url = 'http://weather.noaa.gov/pub/data/observations/metar/decoded/%s.TXT' % station
+ decoded = spawn_read('wget', url, '-O', '-').split('\n')
+ # How to parse: http://www.wunderground.com/metarFAQ.asp
+
+ station_header, self.headers, decoded = decoded[0].split(', '), decoded[:2], decoded[2:]
+ self.station, station_header = station_header[0], ', '.join(station_header[1:])
+ self.location = station_header.split(' (')[0]
+ self.latitude, self.longitude = station_header.split(') ').split(' ')[2:]
+ self.latitude, ysign = self.latitude[:-1], self.latitude[-1] == 'S'
+ self.longitude, xsign = self.longitude[:-1], self.longitude[-1] == 'W'
+ self.latitude = [float(x) for x in self.latitude.split('-')]
+ self.longitude = [float(x) for x in self.longitude.split('-')]
+ self.latitude = self.latitude[0] + self.latitude[1] / 100
+ self.longitude = self.longitude[0] + self.longitude[1] / 100
+ self.latitude = -(self.latitude) if ysign else self.latitude
+ self.longitude = -(self.longitude) if xsign else self.longitude
+
+ self.fields = {}
+ for line in decoded:
+ line = line.split(': ')
+ self.fields[line[0]] = ': '.join(line[1:])
+
+ self.wind_dir, self.wind_speed, self.wind_gusts, self.wind_var = None, None, None, None
+ self.temp, self.dew, self.pressure, self.visibility = None, None, None, None
+
+ for ob in self.fields['ob'].split(' ')[1:]:
+ self.__time(ob)
+ self.__wind(ob)
+ self.__wind_var(ob)
+ self.__temp(ob)
+ self.__pressure(ob)
+ self.__visibility(ob)
+ # (-SHRA)-Present Weather and Obscurations from http://www.wunderground.com/metarFAQ.asp
+ # BKN070-Sky Condition from http://www.wunderground.com/metarFAQ.asp
+
+ #Wind: from the N (010 degrees) at 18 MPH (16 KT):0
+ #Visibility: 3 mile(s):0
+ #Sky conditions: overcast
+ #Weather: precipitation
+ #Precipitation last hour: A trace -- sometimes
+ #Temperature: 10.0 F (-12.2 C)
+ #Windchill: -7 F (-22 C):2 -- sometimes
+ #Dew Point: 3.9 F (-15.6 C)
+ #Relative Humidity: 75%
+ #Pressure (altimeter): 30.19 in. Hg (1022 hPa)
+
+
+ def __time(self, ob):
+ ob_ = list(filter(lambda c : not ('0' <= c <= '9'), ob))
+ if (len(ob_) == 1) and ob.endswith('Z'):
+ self.time = (ob[0 : 2], ob[2 : 4], ob[4 : 6])
+
+ def __wind(self, ob):
+ if ob.endswith('KT'):
+ self.wind_dir = None if ob.startswith('VRB') else float(ob[:3])
+ ob = ob[3:]
+ if self.wind_dir is not None:
+ ob = '0' + ob
+ self.wind_speed, ob = float(ob[:3]), wind[3:]
+ self.wind_gusts = float(ob[1 : -2]) if ob[0] == 'G' else None
+
+ def __wind_var(self, ob):
+ if (len(ob) == 7) and (ob[3] == 'V'):
+ if len(list(filter(lambda c : not ('0' <= c <= '9'), ob))) == 1:
+ self.wind_dir = (float(ob[:3]), float(ob[-3:]))
+
+ def __temp(self, ob):
+ if '/' in ob:
+ ob = ob.replace('M', '-')
+ if len(list(filter(lambda c : not ('0' <= c <= '9'), ob.replace('-', '0')))) == 1:
+ (self.temp, self.dew) = [float(x) for x in ob.split('/')]
+
+ def __pressure(self, ob):
+ if (len(ob) == 5) and (ob[0] in ['Q', 'A']):
+ if len(list(filter(lambda c : not ('0' <= c <= '9'), ob))) == 1:
+ if ob[0] == 'Q':
+ self.pressure = float(ob[1:])
+ else:
+ self.pressure = 33.86 * float(ob[1:]) / 100
+
+ def __visibility(self, ob):
+ if ob.endswith('SM') and (len(ob) > 0):
+ if len(list(filter(lambda c : not ('0' <= c <= '9'), ob))) == 2:
+ self.visibility = float(ob[:-2])
+