barometer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +266 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/barometer +63 -0
  5. data/lib/barometer.rb +52 -0
  6. data/lib/barometer/base.rb +52 -0
  7. data/lib/barometer/data.rb +15 -0
  8. data/lib/barometer/data/current.rb +93 -0
  9. data/lib/barometer/data/distance.rb +131 -0
  10. data/lib/barometer/data/forecast.rb +66 -0
  11. data/lib/barometer/data/geo.rb +98 -0
  12. data/lib/barometer/data/location.rb +20 -0
  13. data/lib/barometer/data/measurement.rb +161 -0
  14. data/lib/barometer/data/pressure.rb +133 -0
  15. data/lib/barometer/data/speed.rb +147 -0
  16. data/lib/barometer/data/sun.rb +35 -0
  17. data/lib/barometer/data/temperature.rb +164 -0
  18. data/lib/barometer/data/units.rb +55 -0
  19. data/lib/barometer/data/zone.rb +124 -0
  20. data/lib/barometer/extensions/graticule.rb +50 -0
  21. data/lib/barometer/extensions/httparty.rb +21 -0
  22. data/lib/barometer/query.rb +228 -0
  23. data/lib/barometer/services.rb +6 -0
  24. data/lib/barometer/services/google.rb +146 -0
  25. data/lib/barometer/services/noaa.rb +6 -0
  26. data/lib/barometer/services/service.rb +324 -0
  27. data/lib/barometer/services/weather_bug.rb +6 -0
  28. data/lib/barometer/services/weather_dot_com.rb +6 -0
  29. data/lib/barometer/services/wunderground.rb +285 -0
  30. data/lib/barometer/services/yahoo.rb +274 -0
  31. data/lib/barometer/weather.rb +187 -0
  32. data/spec/barometer_spec.rb +162 -0
  33. data/spec/data_current_spec.rb +225 -0
  34. data/spec/data_distance_spec.rb +336 -0
  35. data/spec/data_forecast_spec.rb +150 -0
  36. data/spec/data_geo_spec.rb +90 -0
  37. data/spec/data_location_spec.rb +59 -0
  38. data/spec/data_measurement_spec.rb +411 -0
  39. data/spec/data_pressure_spec.rb +336 -0
  40. data/spec/data_speed_spec.rb +374 -0
  41. data/spec/data_sun_spec.rb +76 -0
  42. data/spec/data_temperature_spec.rb +396 -0
  43. data/spec/data_zone_spec.rb +133 -0
  44. data/spec/fixtures/current_calgary_ab.xml +1 -0
  45. data/spec/fixtures/forecast_calgary_ab.xml +1 -0
  46. data/spec/fixtures/geocode_40_73.xml +1 -0
  47. data/spec/fixtures/geocode_90210.xml +1 -0
  48. data/spec/fixtures/geocode_T5B4M9.xml +1 -0
  49. data/spec/fixtures/geocode_calgary_ab.xml +1 -0
  50. data/spec/fixtures/geocode_newyork_ny.xml +1 -0
  51. data/spec/fixtures/google_calgary_ab.xml +1 -0
  52. data/spec/fixtures/yahoo_90210.xml +1 -0
  53. data/spec/query_spec.rb +469 -0
  54. data/spec/service_google_spec.rb +144 -0
  55. data/spec/service_wunderground_spec.rb +330 -0
  56. data/spec/service_yahoo_spec.rb +299 -0
  57. data/spec/services_spec.rb +1106 -0
  58. data/spec/spec_helper.rb +14 -0
  59. data/spec/units_spec.rb +101 -0
  60. data/spec/weather_spec.rb +265 -0
  61. metadata +119 -0
@@ -0,0 +1,6 @@
1
+ module Barometer
2
+
3
+ class WeatherBug < Service
4
+ end
5
+
6
+ end
@@ -0,0 +1,6 @@
1
+ module Barometer
2
+
3
+ class WeatherDotCom < Service
4
+ end
5
+
6
+ end
@@ -0,0 +1,285 @@
1
+ module Barometer
2
+ #
3
+ # = Weather Underground
4
+ # www.wunderground.com
5
+ #
6
+ # - key required: NO
7
+ # - registration required: NO
8
+ # - supported countries: ALL
9
+ #
10
+ # === performs geo coding
11
+ # - city: YES
12
+ # - coordinates: YES
13
+ # NOTE: provides geo data for location and weather station
14
+ #
15
+ # === time info
16
+ # - sun rise/set: YES (today only)
17
+ # - provides timezone: YES
18
+ # - requires TZInfo: YES
19
+ #
20
+ # == resources
21
+ # - API: http://wiki.wunderground.com/index.php/API_-_XML
22
+ #
23
+ # === Possible queries:
24
+ # - http://api.wunderground.com/auto/wui/geo/GeoLookupXML/index.xml?query=94107
25
+ # - http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query=KSFO
26
+ # - http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCASANFR70
27
+ # - http://api.wunderground.com/auto/wui/geo/AlertsXML/index.xml?query=86445
28
+ # - http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml?query=Chicago,IL
29
+ #
30
+ # where query can be:
31
+ # - zipcode (US or Canadian)
32
+ # - city state; city, state
33
+ # - city
34
+ # - state
35
+ # - country
36
+ # - airport code (3-letter or 4-letter)
37
+ # - lat,lon
38
+ #
39
+ class Wunderground < Service
40
+
41
+ def self.accepted_formats
42
+ [:zipcode, :postalcode, :coordinates, :geocode]
43
+ end
44
+
45
+ def self.source_name
46
+ :wunderground
47
+ end
48
+
49
+ # these are the icon codes that indicate "wet", used by wet? function
50
+ def self.wet_icon_codes
51
+ %w(flurries rain sleet snow tstorms nt_flurries nt_rain nt_sleet nt_snow nt_tstorms)
52
+ end
53
+ def self.sunny_icon_codes
54
+ %w(clear mostlysunny partlysunny sunny partlycloudy)
55
+ end
56
+
57
+ def self._measure(measurement, query, metric=true)
58
+ raise ArgumentError unless measurement.is_a?(Barometer::Measurement)
59
+ raise ArgumentError unless query.is_a?(Barometer::Query)
60
+ measurement.source = self.source_name
61
+
62
+ # get current measurement
63
+ begin
64
+ current_result = self.get_current(query.preferred)
65
+ measurement.current = self.build_current(current_result, metric)
66
+ rescue Timeout::Error => e
67
+ return measurement
68
+ end
69
+
70
+ # get forecast measurement
71
+ begin
72
+ forecast_result = self.get_forecast(query.preferred)
73
+ measurement.forecast = self.build_forecast(forecast_result, metric)
74
+ rescue Timeout::Error => e
75
+ return measurement
76
+ end
77
+
78
+ measurement.location = self.build_location(current_result)
79
+ measurement.station = self.build_station(current_result)
80
+ measurement.timezone = self.build_timezone(forecast_result)
81
+
82
+ # add sun data to current
83
+ sun = nil
84
+ if measurement.current
85
+ sun = self.build_sun(forecast_result, measurement.timezone)
86
+ measurement.current.sun = sun
87
+ end
88
+ # use todays sun data for all future days
89
+ if measurement.forecast && sun
90
+ start_date = Date.parse(measurement.current.time)
91
+ measurement.forecast.each do |forecast|
92
+ days_in_future = forecast.date - start_date
93
+ forecast.sun = Barometer::Sun.add_days!(sun,days_in_future.to_i)
94
+ end
95
+ end
96
+
97
+ measurement
98
+ end
99
+
100
+ def self.build_current(data, metric=true)
101
+ raise ArgumentError unless data.is_a?(Hash)
102
+
103
+ current = CurrentMeasurement.new
104
+ #current.time = Time.parse(current_result['observation_time_rfc822']) unless
105
+ # current_result['observation_time_rfc822'].blank?
106
+ current.time = data['observation_time_rfc822']
107
+ current.local_time = data['observation_time']
108
+ current.humidity = data['relative_humidity'].to_i
109
+ current.icon = data['icon'] if data['icon']
110
+
111
+ current.temperature = Temperature.new(metric)
112
+ current.temperature << [data['temp_c'], data['temp_f']]
113
+
114
+ current.wind = Speed.new(metric)
115
+ current.wind.mph = data['wind_mph'].to_f
116
+ current.wind.degrees = data['wind_degrees'].to_i
117
+ current.wind.direction = data['wind_dir']
118
+
119
+ current.pressure = Pressure.new(metric)
120
+ current.pressure << [data['pressure_mb'], data['pressure_in']]
121
+
122
+ current.dew_point = Temperature.new(metric)
123
+ current.dew_point << [data['dewpoint_c'], data['dewpoint_f']]
124
+
125
+ current.heat_index = Temperature.new(metric)
126
+ current.heat_index << [data['heat_index_c'], data['heat_index_f']]
127
+
128
+ current.wind_chill = Temperature.new(metric)
129
+ current.wind_chill << [data['windchill_c'], data['windchill_f']]
130
+
131
+ current.visibility = Distance.new(metric)
132
+ current.visibility << [data['visibility_km'], data['visibility_mi']]
133
+
134
+ current
135
+ end
136
+
137
+ def self.build_forecast(data, metric=true)
138
+ raise ArgumentError unless data.is_a?(Hash)
139
+ forecasts = []
140
+ # go through each forecast and create an instance
141
+ if data && data['simpleforecast'] &&
142
+ data['simpleforecast']['forecastday']
143
+
144
+ data['simpleforecast']['forecastday'].each do |forecast|
145
+ forecast_measurement = ForecastMeasurement.new
146
+ forecast_measurement.icon = forecast['icon']
147
+ forecast_measurement.date = Date.parse(forecast['date']['pretty'])
148
+ forecast_measurement.pop = forecast['pop'].to_i
149
+
150
+ forecast_measurement.high = Temperature.new(metric)
151
+ forecast_measurement.high << [forecast['high']['celsius'],forecast['high']['fahrenheit']]
152
+
153
+ forecast_measurement.low = Temperature.new(metric)
154
+ forecast_measurement.low << [forecast['low']['celsius'],forecast['low']['fahrenheit']]
155
+
156
+ forecasts << forecast_measurement
157
+ end
158
+ end
159
+ forecasts
160
+ end
161
+
162
+ def self.build_location(data)
163
+ raise ArgumentError unless data.is_a?(Hash)
164
+ location = Location.new
165
+ if data['display_location']
166
+ location.name = data['display_location']['full']
167
+ location.city = data['display_location']['city']
168
+ location.state_name = data['display_location']['state_name']
169
+ location.state_code = data['display_location']['state']
170
+ location.country_code = data['display_location']['country']
171
+ location.zip_code = data['display_location']['zip']
172
+ location.latitude = data['display_location']['latitude']
173
+ location.longitude = data['display_location']['longitude']
174
+ end
175
+ location
176
+ end
177
+
178
+ def self.build_station(data)
179
+ raise ArgumentError unless data.is_a?(Hash)
180
+ station = Location.new
181
+ station.id = data['station_id']
182
+ if data['observation_location']
183
+ station.name = data['observation_location']['full']
184
+ station.city = data['observation_location']['city']
185
+ station.state_name = data['observation_location']['state_name']
186
+ station.state_code = data['observation_location']['state']
187
+ station.country_code = data['observation_location']['country']
188
+ station.zip_code = data['observation_location']['zip']
189
+ station.latitude = data['observation_location']['latitude']
190
+ station.longitude = data['observation_location']['longitude']
191
+ end
192
+ station
193
+ end
194
+
195
+ # <forecastday>
196
+ # <date>
197
+ # <pretty_short>9:00 PM CST</pretty_short>
198
+ # <pretty>9:00 PM CST on January 15, 2008</pretty>
199
+ # <isdst>0</isdst>
200
+ # <tz_short>CST</tz_short>
201
+ # <tz_long>America/Chicago</tz_long>
202
+ # </date>
203
+ # </forecastday>
204
+ def self.build_timezone(data)
205
+ raise ArgumentError unless data.is_a?(Hash)
206
+ timezone = nil
207
+ if data && data['simpleforecast'] &&
208
+ data['simpleforecast']['forecastday'] &&
209
+ data['simpleforecast']['forecastday'].first &&
210
+ data['simpleforecast']['forecastday'].first['date']
211
+ timezone = Barometer::Zone.new(
212
+ data['simpleforecast']['forecastday'].first['date']['tz_long']
213
+ )
214
+ end
215
+ timezone
216
+ end
217
+
218
+ def self.build_sun(data, timezone)
219
+ raise ArgumentError unless data.is_a?(Hash)
220
+ raise ArgumentError unless timezone.is_a?(Barometer::Zone)
221
+
222
+ sun = nil
223
+ if data
224
+ time = nil
225
+ if data['simpleforecast'] &&
226
+ data['simpleforecast']['forecastday'] &&
227
+ data['simpleforecast']['forecastday'].first &&
228
+ data['simpleforecast']['forecastday'].first['date']
229
+
230
+ # construct current date
231
+ date_data = data['simpleforecast']['forecastday'].first['date']
232
+ time = Time.local(
233
+ date_data['year'], date_data['month'], date_data['day'],
234
+ date_data['hour'], date_data['min'], date_data['sec']
235
+ )
236
+ end
237
+ if time && data['moon_phase']
238
+ # get the sun rise and set times (ie "6:32 am")
239
+ if data['moon_phase']['sunrise']
240
+ rise = Time.local(
241
+ time.year, time.month, time.day,
242
+ data['moon_phase']['sunrise']['hour'],
243
+ data['moon_phase']['sunrise']['minute']
244
+ )
245
+ end
246
+ if data['moon_phase']['sunset']
247
+ set = Time.local(
248
+ time.year, time.month, time.day,
249
+ data['moon_phase']['sunset']['hour'],
250
+ data['moon_phase']['sunset']['minute']
251
+ )
252
+ end
253
+
254
+ sun = Sun.new(
255
+ timezone.tz.local_to_utc(rise),
256
+ timezone.tz.local_to_utc(set)
257
+ )
258
+ end
259
+ end
260
+
261
+ sun || Sun.new
262
+ end
263
+
264
+ # use HTTParty to get the current weather
265
+ def self.get_current(query)
266
+ Barometer::Wunderground.get(
267
+ "http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml",
268
+ :query => {:query => query},
269
+ :format => :xml,
270
+ :timeout => Barometer.timeout
271
+ )['current_observation']
272
+ end
273
+
274
+ # use HTTParty to get the forecasted weather
275
+ def self.get_forecast(query)
276
+ Barometer::Wunderground.get(
277
+ "http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml",
278
+ :query => {:query => query},
279
+ :format => :xml,
280
+ :timeout => Barometer.timeout
281
+ )['forecast']
282
+ end
283
+
284
+ end
285
+ end
@@ -0,0 +1,274 @@
1
+ module Barometer
2
+ #
3
+ # = Yahoo Weather
4
+ # www.yahoo.com
5
+ #
6
+ # - key required: NO
7
+ # - registration required: NO
8
+ # - supported countries: US (by zipcode), International (by Yahoo Location ID)
9
+ #
10
+ # === performs geo coding
11
+ # - city: YES
12
+ # - coordinates: YES
13
+ #
14
+ # === time info
15
+ # - sun rise/set: YES (today only)
16
+ # - provides timezone: PARTIAL (just short code)
17
+ # - requires TZInfo: NO
18
+ # NOTE: since this only supports US, the short code can be used
19
+ # to convert times (until yahoo location id support is added)
20
+ #
21
+ # == resources
22
+ # - API: http://developer.yahoo.com/weather/
23
+ #
24
+ # === Possible queries:
25
+ # - http://weather.yahooapis.com/forecastrss?p=94089
26
+ # - http://weather.yahooapis.com/forecastrss?p=USCA1116
27
+ # - http://weather.yahooapis.com/forecastrss?p=FRXX0076&u=c
28
+ #
29
+ # where query can be:
30
+ # - zipcode (US)
31
+ # - Yahoo Location ID (International) - not currently supported
32
+ #
33
+ # == notes
34
+ # - the Yahoo Location ID is a propreitary number (possibly shared with weather.com)
35
+ # so this driver currently does not provide a way to get/use this number,
36
+ # therefore International support is currently missing
37
+ #
38
+ class Yahoo < Service
39
+
40
+ def self.accepted_formats
41
+ [:zipcode]
42
+ end
43
+
44
+ def self.source_name
45
+ :yahoo
46
+ end
47
+
48
+ # these are the icon codes that indicate "wet", used by wet? function
49
+ def self.wet_icon_codes
50
+ codes = [1] + (3..18).to_a + [35] + (37..43).to_a + (45..47).to_a
51
+ codes.collect {|c| c.to_s}
52
+ end
53
+ def self.sunny_icon_codes
54
+ codes = (29..34).to_a + [36]
55
+ codes.collect {|c| c.to_s}
56
+ end
57
+
58
+ # override, only currently supports US
59
+ def self.supports_country?(query=nil)
60
+ query && query.country_code && query.country_code.downcase == "us"
61
+ end
62
+
63
+ def self._measure(measurement, query, metric=true)
64
+ raise ArgumentError unless measurement.is_a?(Barometer::Measurement)
65
+ raise ArgumentError unless query.is_a?(Barometer::Query)
66
+ measurement.source = self.source_name
67
+
68
+ begin
69
+ result = self.get_all(query.preferred, metric)
70
+ rescue Timeout::Error => e
71
+ return measurement
72
+ end
73
+
74
+ measurement.current = self.build_current(result, metric)
75
+ measurement.forecast = self.build_forecast(result, metric)
76
+ measurement.location = self.build_location(result, query.geo)
77
+
78
+ # add to current
79
+ sun = nil
80
+ if measurement.current
81
+ sun = self.build_sun(result)
82
+ measurement.current.sun = sun
83
+ end
84
+ # use todays sun data for all future days
85
+ if measurement.forecast && sun
86
+ start_date = Date.parse(measurement.current.local_time)
87
+ measurement.forecast.each do |forecast|
88
+ days_in_future = forecast.date - start_date
89
+ forecast.sun = Barometer::Sun.add_days!(sun,days_in_future.to_i)
90
+ end
91
+ end
92
+
93
+ measurement
94
+ end
95
+
96
+ def self.build_current(data, metric=true)
97
+ raise ArgumentError unless data.is_a?(Hash)
98
+ current = CurrentMeasurement.new
99
+ if data
100
+ if data['item'] && data['item']['yweather:condition']
101
+ condition_result = data['item']['yweather:condition']
102
+ current.local_time = condition_result['date']
103
+ current.icon = condition_result['code']
104
+ current.condition = condition_result['text']
105
+ current.temperature = Temperature.new(metric)
106
+ current.temperature << condition_result['temp']
107
+ end
108
+ if data['yweather:atmosphere']
109
+ atmosphere_result = data['yweather:atmosphere']
110
+ current.humidity = atmosphere_result['humidity'].to_i
111
+ current.pressure = Pressure.new(metric)
112
+ current.pressure << atmosphere_result['pressure']
113
+ current.visibility = Distance.new(metric)
114
+ current.visibility << atmosphere_result['visibility']
115
+ end
116
+ if data['yweather:wind']
117
+ wind_result = data['yweather:wind']
118
+ current.wind = Speed.new(metric)
119
+ current.wind << wind_result['speed']
120
+ current.wind.degrees = wind_result['degrees'].to_f
121
+ current.wind_chill = Temperature.new(metric)
122
+ current.wind_chill << wind_result['chill']
123
+ end
124
+ end
125
+ current
126
+ end
127
+
128
+ def self.build_forecast(data, metric=true)
129
+ raise ArgumentError unless data.is_a?(Hash)
130
+ forecasts = []
131
+
132
+ if data && data['item'] && data['item']['yweather:forecast']
133
+ forecast_result = data['item']['yweather:forecast']
134
+
135
+ forecast_result.each do |forecast|
136
+ forecast_measurement = ForecastMeasurement.new
137
+ forecast_measurement.icon = forecast['code']
138
+ forecast_measurement.date = Date.parse(forecast['date'])
139
+ forecast_measurement.condition = forecast['text']
140
+ forecast_measurement.high = Temperature.new(metric)
141
+ forecast_measurement.high << forecast['high'].to_f
142
+ forecast_measurement.low = Temperature.new(metric)
143
+ forecast_measurement.low << forecast['low'].to_f
144
+ forecasts << forecast_measurement
145
+ end
146
+ end
147
+ forecasts
148
+ end
149
+
150
+ def self.build_location(data, geo=nil)
151
+ raise ArgumentError unless data.is_a?(Hash)
152
+ raise ArgumentError unless (geo.nil? || geo.is_a?(Barometer::Geo))
153
+ location = Location.new
154
+ # use the geocoded data if available, otherwise get data from result
155
+ if geo
156
+ location.city = geo.locality
157
+ location.state_code = geo.region
158
+ location.country = geo.country
159
+ location.country_code = geo.country_code
160
+ location.latitude = geo.latitude
161
+ location.longitude = geo.longitude
162
+ else
163
+ if data && data['yweather:location']
164
+ location.city = data['yweather:location']['city']
165
+ location.state_code = data['yweather:location']['region']
166
+ location.country_code = data['yweather:location']['country']
167
+ if data['item']
168
+ location.latitude = data['item']['geo:lat']
169
+ location.longitude = data['item']['geo:long']
170
+ end
171
+ end
172
+ end
173
+ location
174
+ end
175
+
176
+ def self.build_sun(data)
177
+ raise ArgumentError unless data.is_a?(Hash)
178
+ sun = nil
179
+ if data && data['yweather:astronomy'] && data['item']
180
+ # get the TIME ZONE CODE
181
+ zone_match = data['item']['pubDate'].match(/ ([A-Z]*)$/)
182
+ zone = zone_match[1] if zone_match
183
+ # get the sun rise and set
184
+ rise = Barometer::Zone.merge(
185
+ data['yweather:astronomy']['sunrise'],
186
+ data['item']['pubDate'],
187
+ zone
188
+ )
189
+ set = Barometer::Zone.merge(
190
+ data['yweather:astronomy']['sunset'],
191
+ data['item']['pubDate'],
192
+ zone
193
+ )
194
+ sun = Sun.new(rise, set)
195
+ end
196
+ sun || Sun.new
197
+ end
198
+
199
+ # def self.build_timezone(data)
200
+ # raise ArgumentError unless data.is_a?(Hash)
201
+ #
202
+ # timezone = nil
203
+ # if data && data['simpleforecast'] &&
204
+ # data['simpleforecast']['forecastday'] &&
205
+ # data['simpleforecast']['forecastday'].first &&
206
+ # data['simpleforecast']['forecastday'].first['date']
207
+ # timezone = Barometer::Zone.new(Time.now.utc,data['simpleforecast']['forecastday'].first['date']['tz_long'])
208
+ # end
209
+ # timezone
210
+ # end
211
+
212
+ # use HTTParty to get the current weather
213
+ def self.get_all(query, metric=true)
214
+ Barometer::Yahoo.get(
215
+ "http://weather.yahooapis.com/forecastrss",
216
+ :query => {:p => query, :u => (metric ? 'c' : 'f')},
217
+ :format => :xml,
218
+ :timeout => Barometer.timeout
219
+ )['rss']['channel']
220
+ end
221
+
222
+ end
223
+ end
224
+
225
+ # Condition Codes
226
+ # 0 tornado
227
+ # 1 tropical storm
228
+ # 2 hurricane
229
+ # 3 severe thunderstorms
230
+ # 4 thunderstorms
231
+ # 5 mixed rain and snow
232
+ # 6 mixed rain and sleet
233
+ # 7 mixed snow and sleet
234
+ # 8 freezing drizzle
235
+ # 9 drizzle
236
+ # 10 freezing rain
237
+ # 11 showers
238
+ # 12 showers
239
+ # 13 snow flurries
240
+ # 14 light snow showers
241
+ # 15 blowing snow
242
+ # 16 snow
243
+ # 17 hail
244
+ # 18 sleet
245
+ # 19 dust
246
+ # 20 foggy
247
+ # 21 haze
248
+ # 22 smoky
249
+ # 23 blustery
250
+ # 24 windy
251
+ # 25 cold
252
+ # 26 cloudy
253
+ # 27 mostly cloudy (night)
254
+ # 28 mostly cloudy (day)
255
+ # 29 partly cloudy (night)
256
+ # 30 partly cloudy (day)
257
+ # 31 clear (night)
258
+ # 32 sunny
259
+ # 33 fair (night)
260
+ # 34 fair (day)
261
+ # 35 mixed rain and hail
262
+ # 36 hot
263
+ # 37 isolated thunderstorms
264
+ # 38 scattered thunderstorms
265
+ # 39 scattered thunderstorms
266
+ # 40 scattered showers
267
+ # 41 heavy snow
268
+ # 42 scattered snow showers
269
+ # 43 heavy snow
270
+ # 44 partly cloudy
271
+ # 45 thundershowers
272
+ # 46 snow showers
273
+ # 47 isolated thundershowers
274
+ # 3200 not available