@@ -32,6 +32,7 @@ gem 'kramdown' |
||
| 32 | 32 |
gem "typhoeus" |
| 33 | 33 |
gem 'nokogiri' |
| 34 | 34 |
gem 'wunderground' |
| 35 |
+gem 'forecast_io' |
|
| 35 | 36 |
gem 'rturk' |
| 36 | 37 |
|
| 37 | 38 |
gem "twitter", '~> 5.7.1' |
@@ -109,6 +109,10 @@ GEM |
||
| 109 | 109 |
multipart-post (>= 1.2, < 3) |
| 110 | 110 |
fastercsv (1.5.5) |
| 111 | 111 |
ffi (1.9.0) |
| 112 |
+ forecast_io (2.0.0) |
|
| 113 |
+ faraday |
|
| 114 |
+ hashie |
|
| 115 |
+ multi_json |
|
| 112 | 116 |
foreman (0.63.0) |
| 113 | 117 |
dotenv (>= 0.7) |
| 114 | 118 |
thor (>= 0.13.6) |
@@ -316,6 +320,7 @@ DEPENDENCIES |
||
| 316 | 320 |
dotenv-rails |
| 317 | 321 |
em-http-request |
| 318 | 322 |
fastercsv |
| 323 |
+ forecast_io |
|
| 319 | 324 |
foreman |
| 320 | 325 |
geokit-rails3 |
| 321 | 326 |
jquery-rails |
@@ -6,18 +6,26 @@ module Agents |
||
| 6 | 6 |
cannot_receive_events! |
| 7 | 7 |
|
| 8 | 8 |
description <<-MD |
| 9 |
- The WeatherAgent creates an event for the following day's weather at a given `location`. |
|
| 9 |
+ The WeatherAgent creates an event for the day's weather at a given `location`. |
|
| 10 | 10 |
|
| 11 |
- The `location` can be a US zipcode, or any location that Wunderground supports. To find one, search [wunderground.com](http://wunderground.com) and copy the location part of the URL. For example, a result for San Francisco gives `http://www.wunderground.com/US/CA/San_Francisco.html` and London, England gives `http://www.wunderground.com/q/zmw:00000.1.03772`. The locations in each are `US/CA/San_Francisco` and `zmw:00000.1.03772`, respectively. |
|
| 11 |
+ You also must select `which_day` you would like to get the weather for where the number 0 is for today and 1 is for tomorrow and so on. Weather is only returned for 1 week at a time. |
|
| 12 | 12 |
|
| 13 |
- You must setup an [API key for Wunderground](http://www.wunderground.com/weather/api/) in order to use this Agent. |
|
| 13 |
+ The weather can be provided by either Wunderground or ForecastIO. To choose which `service` to use, enter either `forecastio` or `wunderground`. |
|
| 14 |
+ |
|
| 15 |
+ The `location` can be a US zipcode, or any location that Wunderground supports. To find one, search [wunderground.com](http://wunderground.com) and copy the location part of the URL. For example, a result for San Francisco gives `http://www.wunderground.com/US/CA/San_Francisco.html` and London, England gives `http://www.wunderground.com/q/zmw:00000.1.03772`. The locations in each are `US/CA/San_Francisco` and `zmw:00000.1.03772`, respectively. |
|
| 16 |
+ |
|
| 17 |
+ If you plan on using ForecastIO, the `location` must be a set of GPS coordinates. |
|
| 18 |
+ |
|
| 19 |
+ You must setup an [API key for Wunderground](http://www.wunderground.com/weather/api/) in order to use this Agent with Wunderground. |
|
| 20 |
+ |
|
| 21 |
+ You must setup an [API key for Forecast](https://developer.forecast.io/) in order to use this Agent with ForecastIO. |
|
| 14 | 22 |
MD |
| 15 | 23 |
|
| 16 | 24 |
event_description <<-MD |
| 17 | 25 |
Events look like this: |
| 18 | 26 |
|
| 19 | 27 |
{
|
| 20 |
- "location": 12345, |
|
| 28 |
+ "location": "12345", |
|
| 21 | 29 |
"date": {
|
| 22 | 30 |
"epoch": "1357959600", |
| 23 | 31 |
"pretty": "10:00 PM EST on January 11, 2013" |
@@ -44,38 +52,127 @@ module Agents |
||
| 44 | 52 |
event_created_within?(2) && !recent_error_logs? |
| 45 | 53 |
end |
| 46 | 54 |
|
| 47 |
- def wunderground |
|
| 48 |
- Wunderground.new(options['api_key']) if key_setup? |
|
| 49 |
- end |
|
| 50 |
- |
|
| 51 | 55 |
def key_setup? |
| 52 |
- options['api_key'] && options['api_key'] != "your-key" |
|
| 56 |
+ options['api_key'].present? && options['api_key'] != "your-key" |
|
| 53 | 57 |
end |
| 54 | 58 |
|
| 55 | 59 |
def default_options |
| 56 | 60 |
{
|
| 57 |
- 'api_key' => "your-key", |
|
| 58 |
- 'location' => "94103" |
|
| 61 |
+ 'service' => 'wunderground', |
|
| 62 |
+ 'api_key' => 'your-key', |
|
| 63 |
+ 'location' => '94103', |
|
| 64 |
+ 'which_day' => '1' |
|
| 59 | 65 |
} |
| 60 | 66 |
end |
| 61 | 67 |
|
| 68 |
+ def service |
|
| 69 |
+ options["service"].presence || "wunderground" |
|
| 70 |
+ end |
|
| 71 |
+ |
|
| 72 |
+ def which_day |
|
| 73 |
+ (options["which_day"].presence || 1).to_i |
|
| 74 |
+ end |
|
| 75 |
+ |
|
| 76 |
+ def location |
|
| 77 |
+ options["location"].presence || options["zipcode"] |
|
| 78 |
+ end |
|
| 79 |
+ |
|
| 62 | 80 |
def validate_options |
| 63 |
- errors.add(:base, "location is required") unless options['location'].present? || options['zipcode'].present? |
|
| 64 |
- errors.add(:base, "api_key is required") unless options['api_key'].present? |
|
| 81 |
+ errors.add(:base, "service is required") unless service.present? |
|
| 82 |
+ errors.add(:base, "service must be set to 'forecastio' or 'wunderground'") unless ["forecastio", "wunderground"].include?(service) |
|
| 83 |
+ errors.add(:base, "location is required") unless location.present? |
|
| 84 |
+ errors.add(:base, "api_key is required") unless key_setup? |
|
| 85 |
+ errors.add(:base, "which_day selection is required") unless which_day.present? |
|
| 65 | 86 |
end |
| 66 | 87 |
|
| 67 |
- def check |
|
| 88 |
+ def wunderground |
|
| 89 |
+ Wunderground.new(options['api_key']).forecast_for(location)['forecast']['simpleforecast']['forecastday'] if key_setup? |
|
| 90 |
+ end |
|
| 91 |
+ |
|
| 92 |
+ def forecastio |
|
| 68 | 93 |
if key_setup? |
| 69 |
- wunderground.forecast_for(options['location'] || options['zipcode'])["forecast"]["simpleforecast"]["forecastday"].each do |day| |
|
| 70 |
- if is_tomorrow?(day) |
|
| 71 |
- create_event :payload => day.merge('location' => options['location'] || options['zipcode'])
|
|
| 72 |
- end |
|
| 94 |
+ ForecastIO.api_key = options['api_key'] |
|
| 95 |
+ lat, lng = location.split(',')
|
|
| 96 |
+ ForecastIO.forecast(lat,lng)['daily']['data'] |
|
| 97 |
+ end |
|
| 98 |
+ end |
|
| 99 |
+ |
|
| 100 |
+ def model(service,which_day) |
|
| 101 |
+ if service == "wunderground" |
|
| 102 |
+ wunderground[which_day] |
|
| 103 |
+ elsif service == "forecastio" |
|
| 104 |
+ forecastio.each do |value| |
|
| 105 |
+ timestamp = Time.at(value.time) |
|
| 106 |
+ if (timestamp.to_date - Time.now.to_date).to_i == which_day |
|
| 107 |
+ day = {
|
|
| 108 |
+ 'date' => {
|
|
| 109 |
+ 'epoch' => value.time.to_s, |
|
| 110 |
+ 'pretty' => timestamp.strftime("%l:%M %p %Z on %B %d, %Y"),
|
|
| 111 |
+ 'day' => timestamp.day, |
|
| 112 |
+ 'month' => timestamp.month, |
|
| 113 |
+ 'year' => timestamp.year, |
|
| 114 |
+ 'yday' => timestamp.yday, |
|
| 115 |
+ 'hour' => timestamp.hour, |
|
| 116 |
+ 'min' => timestamp.strftime("%M"),
|
|
| 117 |
+ 'sec' => timestamp.sec, |
|
| 118 |
+ 'isdst' => timestamp.isdst ? 1 : 0 , |
|
| 119 |
+ 'monthname' => timestamp.strftime("%B"),
|
|
| 120 |
+ 'monthname_short' => timestamp.strftime("%b"),
|
|
| 121 |
+ 'weekday_short' => timestamp.strftime("%a"),
|
|
| 122 |
+ 'weekday' => timestamp.strftime("%A"),
|
|
| 123 |
+ 'ampm' => timestamp.strftime("%p"),
|
|
| 124 |
+ 'tz_short' => timestamp.zone |
|
| 125 |
+ }, |
|
| 126 |
+ 'period' => which_day.to_i, |
|
| 127 |
+ 'high' => {
|
|
| 128 |
+ 'fahrenheit' => value.temperatureMax.round().to_s, |
|
| 129 |
+ 'epoch' => value.temperatureMaxTime.to_s, |
|
| 130 |
+ 'fahrenheit_apparent' => value.apparentTemperatureMax.round().to_s, |
|
| 131 |
+ 'epoch_apparent' => value.apparentTemperatureMaxTime.to_s, |
|
| 132 |
+ 'celsius' => ((5*(Float(value.temperatureMax) - 32))/9).round().to_s |
|
| 133 |
+ }, |
|
| 134 |
+ 'low' => {
|
|
| 135 |
+ 'fahrenheit' => value.temperatureMin.round().to_s, |
|
| 136 |
+ 'epoch' => value.temperatureMinTime.to_s, |
|
| 137 |
+ 'fahrenheit_apparent' => value.apparentTemperatureMin.round().to_s, |
|
| 138 |
+ 'epoch_apparent' => value.apparentTemperatureMinTime.to_s, |
|
| 139 |
+ 'celsius' => ((5*(Float(value.temperatureMin) - 32))/9).round().to_s |
|
| 140 |
+ }, |
|
| 141 |
+ 'conditions' => value.summary, |
|
| 142 |
+ 'icon' => value.icon, |
|
| 143 |
+ 'avehumidity' => (value.humidity * 100).to_i, |
|
| 144 |
+ 'sunriseTime' => value.sunriseTime.to_s, |
|
| 145 |
+ 'sunsetTime' => value.sunsetTime.to_s, |
|
| 146 |
+ 'moonPhase' => value.moonPhase.to_s, |
|
| 147 |
+ 'precip' => {
|
|
| 148 |
+ 'intensity' => value.precipIntensity.to_s, |
|
| 149 |
+ 'intensity_max' => value.precipIntensityMax.to_s, |
|
| 150 |
+ 'intensity_max_epoch' => value.precipIntensityMaxTime.to_s, |
|
| 151 |
+ 'probability' => value.precipProbability.to_s, |
|
| 152 |
+ 'type' => value.precipType |
|
| 153 |
+ }, |
|
| 154 |
+ 'dewPoint' => value.dewPoint.to_s, |
|
| 155 |
+ 'avewind' => {
|
|
| 156 |
+ 'mph' => value.windSpeed.round().to_s, |
|
| 157 |
+ 'kph' => (Float(value.windSpeed) * 1.609344).round().to_s, |
|
| 158 |
+ 'degrees' => value.windBearing.to_s |
|
| 159 |
+ }, |
|
| 160 |
+ 'visibility' => value.visibility.to_s, |
|
| 161 |
+ 'cloudCover' => value.cloudCover.to_s, |
|
| 162 |
+ 'pressure' => value.pressure.to_s, |
|
| 163 |
+ 'ozone' => value.ozone.to_s |
|
| 164 |
+ } |
|
| 165 |
+ return day |
|
| 166 |
+ end |
|
| 73 | 167 |
end |
| 74 | 168 |
end |
| 75 | 169 |
end |
| 76 | 170 |
|
| 77 |
- def is_tomorrow?(day) |
|
| 78 |
- Time.zone.at(day["date"]["epoch"].to_i).to_date == Time.zone.now.tomorrow.to_date |
|
| 171 |
+ def check |
|
| 172 |
+ if key_setup? |
|
| 173 |
+ create_event :payload => model(service, which_day).merge('location' => location)
|
|
| 174 |
+ end |
|
| 79 | 175 |
end |
| 176 |
+ |
|
| 80 | 177 |
end |
| 81 | 178 |
end |