require "wwo/version" require "wwo/configuration" require 'hashie' require 'multi_json' require 'faraday' require 'date' module Wwo extend Configuration self.default_params = {} self.api_cache_store = nil class << self # Returns a daily breakdown for weather for the provided start and end data at the # specified location. # def date_range(start_date, end_date, latitude, longitude, forecast_compat = false) start_date_string = start_date.strftime('%F') end_date_string = end_date.strftime('%F') today_date_string = Time.now.strftime('%F') if start_date_string == today_date_string || range_in_future?(start_date, end_date) uri = "#{Wwo.api_endpoint}/weather.ashx?q=#{latitude},#{longitude}&format=json&num_of_days=7&date=today&cc=no&mca=no&tp=24&key=#{Wwo.api_key}" response = Hashie::Mash.new(api_call(uri)) if forecast_compat return make_into_forecast_response(response) else return response end elsif starts_in_past_but_ends_in_future?(start_date, end_date) yesterday_date_string = (Time.now - (24 *(60 * 60))).strftime('%F') uri = "#{Wwo.api_endpoint}/past-weather.ashx?q=#{latitude},#{longitude}&format=json&extra=utcDateTime&date=#{start_date_string}&enddate=#{yesterday_date_string}&show_comments=no&tp=24&key=#{Wwo.api_key}&mca=false&show_comments=false" past_response = Hashie::Mash.new(api_call(uri)) uri = "#{Wwo.api_endpoint}/weather.ashx?q=#{latitude},#{longitude}&format=json&num_of_days=7&date=today&cc=no&mca=no&tp=24&key=#{Wwo.api_key}" future_response = Hashie::Mash.new(api_call(uri)) if forecast_compat past = make_into_forecast_response(past_response) future = make_into_forecast_response(future_response) past[:daily][:data] = (past[:daily][:data] + future[:daily][:data]).flatten.uniq return past else return past_response.deep_merge(future_response) end else uri = "#{Wwo.api_endpoint}/past-weather.ashx?q=#{latitude},#{longitude}&format=json&extra=utcDateTime&date=#{start_date_string}&enddate=#{end_date_string}&show_comments=no&tp=24&key=#{Wwo.api_key}&mca=false&show_comments=false" response = Hashie::Mash.new(api_call(uri)) if forecast_compat return make_into_forecast_response(response) else return response end end end def starts_in_past_but_ends_in_future?(start_date, end_date) start_date.to_i < Time.now.to_i && end_date.to_i >= Time.now.to_i end def range_in_future?(start_date, end_date) end_date.to_i >= Time.now.to_i && start_date.to_i >= Time.now.to_i end # Returns an hourly breakdown for the weather "today" at the given location. We get # the current time and then turn it into UTC. Returns a Hashie Mash with every hour of # weather broken down. # def today(latitude, longitude) date = Time.now.utc.strftime("%F") uri = "#{Wwo.api_endpoint}/weather.ashx?q=#{latitude},#{longitude}&date=today&num_of_days=1&tp=1&format=json&key=#{Wwo.api_key}&mca=false&show_comments=false" api_call(uri) end # Provides API compatibility to forecast.io's rubygem - expects the same signature and a # Unix Timestamp for :time, it will use the historic / now_or_later methods under the hood # to actually do its work. # def forecast(latitude, longitude, options = {}) if options[:time] date = Time.at(options[:time]) else date = Time.now end if date.to_i < Time.now.to_i make_into_forecast_response(historic(latitude, longitude, date)) else make_into_forecast_response(now_or_later(latitude, longitude, date)) end end # Returns historic weather at the provided latitude and longitude coordinates, on a # specific date. # # @param latitude [String] Latitude. # @param longitude [String] Longitude. # @param date [Date] or [Integer] Date, or Unix Timestamp. # def historic(latitude, longitude, date_or_timestamp) date = date_or_timestamp.is_a?(Numeric) ? Time.at(date_or_timestamp).strftime("%F") : date_or_timestamp.strftime("%F") uri = "#{Wwo.api_endpoint}/past-weather.ashx?q=#{latitude},#{longitude}&date=#{date}&tp=24&format=json&key=#{Wwo.api_key}" api_call(uri) end # Returns historic weather at the provided latitude and longitude coordinates, on a # specific date. # # @param latitude [String] Latitude. # @param longitude [String] Longitude. # @param date [Date] or [Integer] Date, or Unix Timestamp. # def now_or_later(latitude, longitude, date_or_timestamp = Date.today) date = date_or_timestamp.is_a?(Numeric) ? Time.at(date_or_timestamp).strftime("%F") : date_or_timestamp.strftime("%F") uri = "#{Wwo.api_endpoint}/weather.ashx?q=#{latitude},#{longitude}&date=#{date}&num_of_days=1&tp=24&format=json&key=#{Wwo.api_key}" api_call(uri) end # Build or get an HTTP connection object. def connection return @connection if @connection @connection = Faraday.new end # Set an HTTP connection object. # # @param connection Connection object to be used. def connection=(connection) @connection = connection end private def api_call(uri) api_response = get(uri) if api_response.success? return Hashie::Mash.new(MultiJson.load(api_response.body)) else return {} end end def get(path, params = {}) params = Wwo.default_params.merge(params || {}) connection.get(path, params) end # Munges the repsonse into one like what we would expect from Forecast.io # def make_into_forecast_response(response) if response.is_a?(Hash) && response.empty? return { daily: { data: [] } } elsif ! response.data.weather.nil? && response.data.weather.any? && response.data.weather.size > 1 data = { daily: { data: [] } } response.data.weather.each do |weather| icon = weather.hourly.first.weatherIconUrl.first.value maxTemp = weather.maxtempC minTemp = weather.mintempC condition = weather.weatherCode date = Time.parse("#{weather.date} 12:00:00") data[:daily][:data] << { icon: icon, "temperatureMax" => maxTemp, "temperatureMin" => minTemp, date: date, conditionCode: condition } end return data else data = { daily: { data: [ { icon: '', 'temperatureMax' => 0, 'temperatureMin' => 0 } ] }, alerts: nil } data[:daily][:data][0][:icon] = response.data.weather.first.hourly.first.weatherIconUrl.first.value data[:daily][:data][0]['temperatureMax'] = response.data.weather.first.maxtempC data[:daily][:data][0]['temperatureMin'] = response.data.weather.first.mintempC data[:daily][:data][0]["conditionCode"] = response.data.weather.first.weatherCode return data end end end end