Skip to content

Instantly share code, notes, and snippets.

@mathias
Last active December 30, 2015 22:43
Show Gist options
  • Save mathias/7294236 to your computer and use it in GitHub Desktop.
Save mathias/7294236 to your computer and use it in GitHub Desktop.

Forked from the dashing Verbinski plugin, which uses the excellent Forecast.io API.

<div class="gridster">
<ul>
<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
<div data-id="weather_now" data-view="WeatherNow" data-bind-class="right_now_bg"></div>
</li>
<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
<div data-id="weather_today" data-view="WeatherToday" data-bind-class="today_bg"></div>
</li>
<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
<div data-id="weather_week" data-view="WeatherWeek" data-bind-class="this_week_bg"></div>
</li>
</ul>
</div>
require 'date'
require 'net/https'
require 'json'
require 'dotenv'
Dotenv.load
# Forecast API Key from https://developer.forecast.io
forecast_api_key = ENV['FORECAST_API_KEY']
# Latitude, Longitude for location
forecast_location_lat = ENV['LATITUDE']
forecast_location_long = ENV['LONGITUDE']
# Unit Format
#forecast_units = "ca" # like "si", except windSpeed is in kph
forecast_units = "auto"
def time_to_str(time_obj)
""" format: 5 pm """
return Time.at(time_obj).strftime "%-l %P"
end
def time_to_str_minutes(time_obj)
""" format: 5:38 pm """
return Time.at(time_obj).strftime "%-l:%M %P"
end
def day_to_str(time_obj)
""" format: Sun """
return Time.at(time_obj).strftime "%a"
end
SCHEDULER.every '10m', :first_in => 0 do |job|
http = Net::HTTP.new("api.forecast.io", 443)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
response = http.request(Net::HTTP::Get.new("/forecast/#{forecast_api_key}/#{forecast_location_lat},#{forecast_location_long}?units=#{forecast_units}"))
forecast = JSON.parse(response.body)
raise "Forecast.io: " + forecast["error"] if forecast.has_key?("error")
currently = forecast["currently"] || {}
current = {
temperature: currently.fetch("temperature", 0).round,
summary: currently["summary"],
humidity: "#{(currently.fetch("humidity", 0) * 100).round}&#37;",
wind_speed: currently.fetch("windSpeed", 0).round,
wind_bearing: currently.fetch("windSpeed", 0).round == 0 ? 0 : currently["windBearing"],
icon: currently["icon"]
}
daily = forecast["daily"]["data"][0]
today = {
summary: forecast["hourly"]["summary"],
high: daily["temperatureMax"].round,
low: daily["temperatureMin"].round,
sunrise: time_to_str_minutes(daily["sunriseTime"]),
sunset: time_to_str_minutes(daily["sunsetTime"]),
icon: daily["icon"]
}
this_week = []
for day in (1..7)
day = forecast["daily"]["data"][day]
this_day = {
max_temp: day["temperatureMax"].round,
min_temp: day["temperatureMin"].round,
time: day_to_str(day["time"]),
icon: day["icon"]
}
this_week.push(this_day)
end
send_event('weather_now', {
current: current
})
send_event('weather_today', {
today: today,
})
send_event('weather_week', {
upcoming_week: this_week
})
end
console.log("Verbinski (the weather widget) has been loaded!");
function getIcon(iconKey) {
var prefix = "/assets/climacons/svg/";
var ext = ".svg";
if (!iconKey) {
return prefix + "Cloud-Refresh" + ext;
}
var key = iconKey.replace(/-/g, "_");
var iconMap = {
clear_day: "Sun",
clear_night: "Moon",
rain: "Cloud-Rain",
snow: "Snowflake",
sleet: "Cloud-Hail",
wind: "Wind",
fog: "Cloud-Fog-Alt",
cloudy: "Cloud",
partly_cloudy_day: "Cloud-Sun",
partly_cloudy_night: "Cloud-Moon"
}
var fullPath = prefix + iconMap[key] + ext;
return fullPath;
}
function getWindDirection(windBearing) {
// windBearing is where the wind is coming FROM
var direction;
if ((windBearing > 315) || (windBearing < 45)) {
direction = "S";
} else if ((windBearing >= 45) && (windBearing < 135)) {
direction = "W";
} else if ((windBearing >= 135) && (windBearing < 225)) {
direction = "N";
} else {
direction = "E";
}
return direction;
}
class Dashing.WeatherNow extends Dashing.Widget
@accessor 'current_icon', ->
getIcon(@get('current.icon'))
ready: ->
# This is fired when the widget is done being rendered
onData: (data) ->
# Handle incoming data
@currentBg(@get('current.temperature'))
@getWindDirection(@get('current.wind_bearing'))
# flash the html node of this widget each time data comes in
$(@node).fadeOut().fadeIn()
currentBg: (temp) ->
@set 'right_now_bg', "widget widget-weather-now " + WeatherShared.getBackground(temp)
getWindDirection: (windBearing) ->
@set 'wind_bearing', getWindDirection(windBearing)
<h3 class="title">Right now</h3>
<h2 class="temp" data-bind="current.temperature | append '&deg;' | raw"></h2>
<div class="summary-wrapper clearfix">
<img class="weather-icon-left" data-bind-src="current_icon" />
<p class="summary summary-right" data-bind="current.summary | raw"></p>
</div>
<div class="details-text-only">
<p>
<span>Humidity: </span>
<span data-bind="current.humidity | raw"></span>
</p>
<p>
<span>Wind: </span>
<span data-bind="wind_bearing"></span>
<span data-bind="current.wind_speed | raw"></span>
<span> mph </span>
</p>
</div>
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
// text colour
$full-colour: rgba(255, 255, 255, 1);
$light-colour: rgba(255, 255, 255, 0.7);
$dark-colour: rgba(0, 0, 0, 0.3);
// text sizes
$info: 18px;
$info-small: 16px;
// ----------------------------------------------------------------------------
// Widget-verbinski styles
// ----------------------------------------------------------------------------
.widget-weather-now {
.title {
color: $light-colour;
text-transform: uppercase;
}
.temp { color: $full-colour; }
.summary {
color: $full-colour;
text-align: left;
}
// icon + summary
.summary-wrapper {
display: table;
margin: 0 10px;
.weather-icon-left {
max-width: none;
padding: -10px;
}
.summary-right {
display: table-cell;
vertical-align: middle;
font-size: $info;
}
}
.details-text-only {
font-size: $info;
margin-top: 10px;
}
.upcoming-week {
margin: 15px 0 15px 50px;
text-align: left;
p {
font-size: $info;
margin: -5px 0;
.weather-icon-inline {
max-width: 50px;
margin: -5px;
}
.weather-icon-inline.large {
max-width: 60px;
margin: -10px;
}
}
}
}
window.WeatherShared =
getBackground: (temp) ->
range =
0: -10
1: [-10..0]
2: [1..32]
3: [33..44]
4: [45..54]
5: [55..64]
6: [65..74]
7: [75..89]
8: 90
weather = "#4b4b4b"
switch
when temp <= range[0] then weather = 'cold5'
when temp in range[1] then weather = 'cold4'
when temp in range[2] then weather = 'cold3'
when temp in range[3] then weather = 'cold2'
when temp in range[4] then weather = 'cold1'
when temp in range[5] then weather = 'cool2'
when temp in range[6] then weather = 'cool1'
when temp in range[7] then weather = 'warm'
when temp >= range[8] then weather = 'hot'
weather
// column colours
$cold5: #173A7F; // -20 and under
$cold4: #1E4BA6; // -19 to -11
$cold3: #2358C2; // -10 to -1
$cold2: #2863DB; // 0 to 4
$cold1: #2F74FF; // 5 to 9
$cool2: #25D6A4; // 10 to 14
$cool1: #B9CC5A; // 15 to 19
$warm: #ff9d37;// 20 to 24
$hot: #ff5131; // 25 and above
// ----------------------------------------------------------------------------
// Widget-verbinski styles
// ----------------------------------------------------------------------------
.cold5 { background-color: $cold5; }
.cold4 { background-color: $cold4; }
.cold3 { background-color: $cold3; }
.cold2 { background-color: $cold2; }
.cold1 { background-color: $cold1; }
.cool2 { background-color: $cool2; }
.cool1 { background-color: $cool1; }
.warm { background-color: $warm; }
.hot { background-color: $hot; }
class Dashing.WeatherToday extends Dashing.Widget
@accessor 'day_icon', ->
getIcon(@get('today.icon'))
ready: ->
# This is fired when the widget is done being rendered
onData: (data) ->
# Handle incoming data
@todayBg(@get('today.high'), @get('today.low'))
@getTime()
# flash the html node of this widget each time data comes in
$(@node).fadeOut().fadeIn()
currentBg: (temp) ->
@set 'right_now', @getBackground(temp)
todayBg: (high, low) ->
averageRaw = (high + low) / 2
average = Math.round(averageRaw)
@set 'today_bg', "widget widget-weather-today " + WeatherShared.getBackground(average)
getTime: (now = new Date()) ->
hour = now.getHours()
minutes = now.getMinutes()
minutes = if minutes < 10 then "0#{minutes}" else minutes
ampm = if hour >= 12 then "pm" else "am"
hour12 = if hour % 12 then hour % 12 else 12
@set 'last_updated', "#{hour12}:#{minutes} #{ampm}"
<h3 class="title">Today</h3>
<div class="summary-wrapper clearfix">
<img class="weather-icon-left" data-bind-src="day_icon" />
<p class="summary summary-right" data-bind="today.summary"></p>
</div>
<div class="details-text-only">
<p>
<span>L&thinsp;: </span>
<span data-bind="today.low | append '&deg;F' | raw"></span>
<span> | </span>
<span>H&thinsp;: </span>
<span data-bind="today.high | append '&deg;F' | raw"></span>
</p>
</div>
<div class="details-text-only">
<p>
<span>Sunrise: </span><span data-bind="today.sunrise"></span>
</p>
<p>
<span>Sunset: </span><span data-bind="today.sunset"></span>
</p>
</div>
<p class="powered-by">Powered by Forecast.io</p>
<p class="last-updated">
<span>Last updated: </span>
<span data-bind="last_updated"></span>
</p>
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
// text colour
$full-colour: rgba(255, 255, 255, 1);
$light-colour: rgba(255, 255, 255, 0.7);
$dark-colour: rgba(0, 0, 0, 0.3);
// text sizes
$info: 18px;
$info-small: 16px;
// ----------------------------------------------------------------------------
// Widget-verbinski styles
// ----------------------------------------------------------------------------
.widget-weather-today {
.title {
color: $light-colour;
text-transform: uppercase;
}
.temp { color: $full-colour; }
.summary {
color: $full-colour;
text-align: left;
}
// icon + summary
.summary-wrapper {
.weather-icon-left {
max-width: none;
float: left;
}
.summary-right {
max-height: 120px;
overflow: hidden;
vertical-align: middle;
font-size: $info;
}
}
.details-text-only {
font-size: $info;
margin-top: 10px;
}
.upcoming-week {
margin: 15px 0 15px 50px;
text-align: left;
p {
font-size: $info;
margin: -5px 0;
.weather-icon-inline {
max-width: 50px;
margin: -5px;
}
.weather-icon-inline.large {
max-width: 60px;
margin: -10px;
}
}
}
.powered-by {
color: $light-colour;
font-size: $info-small;
}
.last-updated {
font-size: $info-small;
color: $dark-colour;
}
}
class Dashing.WeatherWeek extends Dashing.Widget
ready: ->
# This is fired when the widget is done being rendered
onData: (data) ->
# Handle incoming data
@thisWeekBg(@get('upcoming_week'))
@unpackWeek(@get('upcoming_week'))
# flash the html node of this widget each time data comes in
$(@node).fadeOut().fadeIn()
thisWeekBg: (weekRange) ->
averages = []
for day in weekRange
average = Math.round((day.max_temp + day.min_temp) / 2)
averages.push average
sum = 0
averages.forEach (a) -> sum += a
weekAverage = Math.round(sum / 7)
@set 'this_week_bg', "widget widget-weather-week " + WeatherShared.getBackground(weekAverage)
unpackWeek: (thisWeek) ->
# get max temp, min temp, icon for the next seven days
days = []
for day in thisWeek
dayObj = {
time: day['time'],
min_temp: "#{day['min_temp']}&deg;",
max_temp: "#{day['max_temp']}&deg;",
icon: getIcon(day['icon'])
}
days.push dayObj
@set 'this_week', days
<h3 class="title">This week</h3>
<div class="upcoming-week">
<p data-foreach-day="this_week">
<span data-bind="day.time"></span>:
<span data-bind="day.min_temp | raw"></span> -
<span data-bind="day.max_temp | raw"></span>
<img class="weather-icon-inline" data-bind-src="day.icon" />
</p>
</div>
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
// text colour
$full-colour: rgba(255, 255, 255, 1);
$light-colour: rgba(255, 255, 255, 0.7);
$dark-colour: rgba(0, 0, 0, 0.3);
// text sizes
$info: 18px;
$info-small: 16px;
// ----------------------------------------------------------------------------
// Widget-verbinski styles
// ----------------------------------------------------------------------------
.widget-weather-week {
.title {
color: $light-colour;
text-transform: uppercase;
}
.temp { color: $full-colour; }
.summary {
color: $full-colour;
text-align: left;
}
// icon + summary
.summary-wrapper {
display: table;
margin: 0 10px;
.weather-icon-left {
max-width: none;
padding: -10px;
}
.summary-right {
display: table-cell;
vertical-align: middle;
font-size: $info;
}
}
.details-text-only {
font-size: $info;
margin-top: 10px;
}
.upcoming-week {
text-align: center;
p {
font-size: $info;
margin: -5px 0;
.weather-icon-inline {
max-width: 50px;
margin: -5px;
}
.weather-icon-inline.large {
max-width: 60px;
margin: -10px;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment