Module: Timely::Weather
- Defined in:
- lib/timely/weather.rb
Constant Summary collapse
- SYMBOLS =
{ 0 => "\u2600", # Clear sky 1 => "\u{1F324}", # Mostly clear 2 => "\u26C5", # Partly cloudy 3 => "\u2601", # Cloudy 5 => "\u{1F326}", # Rain showers 6 => "\u{1F327}", # Rain 8 => "\u{1F328}", # Snow 9 => "\u{1F329}", # Thunder }.freeze
Class Method Summary collapse
-
.fetch(lat, lon, db = nil) ⇒ Object
Fetch weather forecast from met.no Returns hash of date_str => { temp_high:, temp_low:, symbol:, wind:, description: }.
-
.short_for_date(forecast, date) ⇒ Object
Get a short weather string for a date: "☀ 12°".
Class Method Details
.fetch(lat, lon, db = nil) ⇒ Object
Fetch weather forecast from met.no Returns hash of date_str => { temp_high:, temp_low:, symbol:, wind:, description: }
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/timely/weather.rb', line 20 def self.fetch(lat, lon, db = nil) # Check cache first (6 hour TTL) if db cached = db.execute( "SELECT data, fetched_at FROM weather_cache WHERE date = 'forecast' LIMIT 1" ).first if cached && (Time.now.to_i - cached['fetched_at'].to_i) < 21600 return JSON.parse(cached['data']) rescue {} end end uri = URI("https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=#{lat}&lon=#{lon}") req = Net::HTTP::Get.new(uri) req['User-Agent'] = 'timely-calendar/0.1 [email protected]' req['Accept-Encoding'] = 'identity' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, read_timeout: 10, open_timeout: 5) do |http| http.request(req) end return {} unless res.is_a?(Net::HTTPSuccess) series = JSON.parse(res.body).dig('properties', 'timeseries') || [] # Group by date, find high/low temps and midday conditions by_date = {} series.each do |ts| det = ts.dig('data', 'instant', 'details') next unless det time = ts['time'] date = time[0..9] hour = time[11..12].to_i temp = det['air_temperature'].to_f by_date[date] ||= { temps: [], wind: 0, cloud: 0, midday_temp: nil } by_date[date][:temps] << temp # Capture midday (12:00) conditions for the symbol if hour == 12 by_date[date][:midday_temp] = temp by_date[date][:wind] = det['wind_speed'].to_f.round(1) by_date[date][:cloud] = det['cloud_area_fraction'].to_i end end forecast = {} by_date.each do |date, data| cloud = data[:cloud] symbol = if cloud < 15 SYMBOLS[0] elsif cloud < 40 SYMBOLS[1] elsif cloud < 70 SYMBOLS[2] else SYMBOLS[3] end temps = data[:temps] forecast[date] = { 'temp_high' => temps.max.round(1), 'temp_low' => temps.min.round(1), 'temp_mid' => (data[:midday_temp] || temps[temps.size / 2]).round(1), 'symbol' => symbol, 'wind' => data[:wind], 'cloud' => cloud } end # Cache result if db db.execute( "INSERT OR REPLACE INTO weather_cache (date, hour, data, fetched_at) VALUES (?, ?, ?, ?)", ['forecast', '00', JSON.generate(forecast), Time.now.to_i] ) end forecast rescue SocketError, Timeout::Error, Net::OpenTimeout, Errno::ECONNREFUSED => e {} end |
.short_for_date(forecast, date) ⇒ Object
Get a short weather string for a date: "☀ 12°"
105 106 107 108 109 110 |
# File 'lib/timely/weather.rb', line 105 def self.short_for_date(forecast, date) date_str = date.strftime('%Y-%m-%d') w = forecast[date_str] return nil unless w "#{w['symbol']} #{w['temp_mid']}°" end |