weather_agent.rb 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. require 'date'
  2. require 'cgi'
  3. module Agents
  4. class WeatherAgent < Agent
  5. cannot_receive_events!
  6. gem_dependency_check { defined?(Wunderground) && defined?(ForecastIO) }
  7. description <<-MD
  8. #{'## Include `forecast_io` and `wunderground` in your Gemfile to use this Agent!' if dependencies_missing?}
  9. The WeatherAgent creates an event for the day's weather at a given `location`.
  10. 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.
  11. The weather can be provided by either Wunderground or ForecastIO. To choose which `service` to use, enter either `forecastio` or `wunderground`.
  12. 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.
  13. If you plan on using ForecastIO, the `location` must be a comma-separated string of co-ordinates (longitude, latitude). For example, San Francisco would be `37.7771,-122.4196`.
  14. You must setup an [API key for Wunderground](http://www.wunderground.com/weather/api/) in order to use this Agent with Wunderground.
  15. You must setup an [API key for Forecast](https://developer.forecast.io/) in order to use this Agent with ForecastIO.
  16. Set `expected_update_period_in_days` to the maximum amount of time that you'd expect to pass between Events being created by this Agent.
  17. MD
  18. event_description <<-MD
  19. Events look like this:
  20. {
  21. "location": "12345",
  22. "date": {
  23. "epoch": "1357959600",
  24. "pretty": "10:00 PM EST on January 11, 2013"
  25. },
  26. "high": {
  27. "fahrenheit": "64",
  28. "celsius": "18"
  29. },
  30. "low": {
  31. "fahrenheit": "52",
  32. "celsius": "11"
  33. },
  34. "conditions": "Rain Showers",
  35. "icon": "rain",
  36. "icon_url": "http://icons-ak.wxug.com/i/c/k/rain.gif",
  37. "skyicon": "mostlycloudy",
  38. ...
  39. }
  40. MD
  41. default_schedule "8pm"
  42. def working?
  43. event_created_within?((interpolated['expected_update_period_in_days'].presence || 2).to_i) && !recent_error_logs?
  44. end
  45. def key_setup?
  46. interpolated['api_key'].present? && interpolated['api_key'] != "your-key"
  47. end
  48. def default_options
  49. {
  50. 'service' => 'wunderground',
  51. 'api_key' => 'your-key',
  52. 'location' => '94103',
  53. 'which_day' => '1',
  54. 'expected_update_period_in_days' => '2'
  55. }
  56. end
  57. def service
  58. interpolated["service"].presence || "wunderground"
  59. end
  60. def which_day
  61. (interpolated["which_day"].presence || 1).to_i
  62. end
  63. def location
  64. interpolated["location"].presence || interpolated["zipcode"]
  65. end
  66. def validate_options
  67. errors.add(:base, "service is required") unless service.present?
  68. errors.add(:base, "service must be set to 'forecastio' or 'wunderground'") unless ["forecastio", "wunderground"].include?(service)
  69. errors.add(:base, "location is required") unless location.present?
  70. errors.add(:base, "api_key is required") unless key_setup?
  71. errors.add(:base, "which_day selection is required") unless which_day.present?
  72. end
  73. def wunderground
  74. Wunderground.new(interpolated['api_key']).forecast_for(location)['forecast']['simpleforecast']['forecastday'] if key_setup?
  75. end
  76. def forecastio
  77. if key_setup?
  78. ForecastIO.api_key = interpolated['api_key']
  79. lat, lng = location.split(',')
  80. ForecastIO.forecast(lat,lng)['daily']['data']
  81. end
  82. end
  83. def model(service,which_day)
  84. if service == "wunderground"
  85. wunderground[which_day]
  86. elsif service == "forecastio"
  87. forecastio.each do |value|
  88. timestamp = Time.at(value.time)
  89. if (timestamp.to_date - Time.now.to_date).to_i == which_day
  90. day = {
  91. 'date' => {
  92. 'epoch' => value.time.to_s,
  93. 'pretty' => timestamp.strftime("%l:%M %p %Z on %B %d, %Y"),
  94. 'day' => timestamp.day,
  95. 'month' => timestamp.month,
  96. 'year' => timestamp.year,
  97. 'yday' => timestamp.yday,
  98. 'hour' => timestamp.hour,
  99. 'min' => timestamp.strftime("%M"),
  100. 'sec' => timestamp.sec,
  101. 'isdst' => timestamp.isdst ? 1 : 0 ,
  102. 'monthname' => timestamp.strftime("%B"),
  103. 'monthname_short' => timestamp.strftime("%b"),
  104. 'weekday_short' => timestamp.strftime("%a"),
  105. 'weekday' => timestamp.strftime("%A"),
  106. 'ampm' => timestamp.strftime("%p"),
  107. 'tz_short' => timestamp.zone
  108. },
  109. 'period' => which_day.to_i,
  110. 'high' => {
  111. 'fahrenheit' => value.temperatureMax.round().to_s,
  112. 'epoch' => value.temperatureMaxTime.to_s,
  113. 'fahrenheit_apparent' => value.apparentTemperatureMax.round().to_s,
  114. 'epoch_apparent' => value.apparentTemperatureMaxTime.to_s,
  115. 'celsius' => ((5*(Float(value.temperatureMax) - 32))/9).round().to_s
  116. },
  117. 'low' => {
  118. 'fahrenheit' => value.temperatureMin.round().to_s,
  119. 'epoch' => value.temperatureMinTime.to_s,
  120. 'fahrenheit_apparent' => value.apparentTemperatureMin.round().to_s,
  121. 'epoch_apparent' => value.apparentTemperatureMinTime.to_s,
  122. 'celsius' => ((5*(Float(value.temperatureMin) - 32))/9).round().to_s
  123. },
  124. 'conditions' => value.summary,
  125. 'icon' => value.icon,
  126. 'avehumidity' => (value.humidity * 100).to_i,
  127. 'sunriseTime' => value.sunriseTime.to_s,
  128. 'sunsetTime' => value.sunsetTime.to_s,
  129. 'moonPhase' => value.moonPhase.to_s,
  130. 'precip' => {
  131. 'intensity' => value.precipIntensity.to_s,
  132. 'intensity_max' => value.precipIntensityMax.to_s,
  133. 'intensity_max_epoch' => value.precipIntensityMaxTime.to_s,
  134. 'probability' => value.precipProbability.to_s,
  135. 'type' => value.precipType
  136. },
  137. 'dewPoint' => value.dewPoint.to_s,
  138. 'avewind' => {
  139. 'mph' => value.windSpeed.round().to_s,
  140. 'kph' => (Float(value.windSpeed) * 1.609344).round().to_s,
  141. 'degrees' => value.windBearing.to_s
  142. },
  143. 'visibility' => value.visibility.to_s,
  144. 'cloudCover' => value.cloudCover.to_s,
  145. 'pressure' => value.pressure.to_s,
  146. 'ozone' => value.ozone.to_s
  147. }
  148. return day
  149. end
  150. end
  151. end
  152. end
  153. def check
  154. if key_setup?
  155. create_event :payload => model(service, which_day).merge('location' => location)
  156. end
  157. end
  158. end
  159. end