Merge branch 'weather'

This commit is contained in:
Wesley Moore 2010-09-26 17:22:23 +10:00
commit cd35c78029
19 changed files with 325 additions and 5 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@ output/*/*/*/*/*.html
output/fonts/*
tmp/*
.*.swp
output/weather.json

5
Rules
View file

@ -5,6 +5,11 @@ compile '/' do
filter :rubypants
end
compile '/weather/' do
layout 'weather'
filter :erb
end
compile %r{/(?:technical|personal|)articles/(page/|)} do
filter :erb
layout 'articles'

View file

@ -14,3 +14,25 @@ written in Lua. The Lua one is the one currently in use.
[flickr]: http://www.flickr.com/photos/wezm/
[github]: http://github.com/wezm
Weather Station
---------------
I have a weather station at my home for keeping track of the local conditions.
I added support for [SQLite][sqlite] logging to [my fork][open2300fork] of the
[Open2300][open2300] project. The weather station is connected to our TV
computer, which is a Mac mini. Periodically it logs the current conditions,
uploads them to my server and invokes a [lua program][weather-tools] to generate
a [JSON][json] file, which is used on the [Weather page][weather].
[sqlite]: http://www.sqlite.org/
[open2300]: http://www.lavrsen.dk/foswiki/bin/view/Open2300/WebHome
[open2300fork]: http://github.com/wezm/open2300
[weather]: http://weather.wezm.net/
[json]: http://www.json.org/
[weather-tools]: http://github.com/wezm/weather-tools
See the following posts for more information on the weather station:
* [Weather Station Install](/personal/2010/09/weather-station/)
* [Weather Station Software Update](/technical/2010/09/weather-station-software/)

View file

@ -163,7 +163,7 @@ body > header
margin-right: 40%
body.articles #content
body.articles #content, body.weather #content
min-width: 0
margin-right: 0
@ -417,3 +417,34 @@ table
.vcard .photo
margin: 0 1em 1em 0
// Weather
body.weather
#current > .temperature
//margin-left: 1em
.temperature
font-family: "Helvetica Neue", Helvetica, "Liberation Sans", "Bitstream Vera Sans", Tahoma, Geneva, Arial, sans-serif
font-weight: 200
.current
font-size: 36px
figure.forecast
margin: 0 1em 1em 0
width: 48px
float: left
.chart
clear: both
.canvas
width: 100%
height: 320px
small
display: block
font-size: smaller
margin-top: 1em
color: #999

View file

@ -0,0 +1,37 @@
Two weeks ago when I [installed my weather station][install] I setup the wview
software to log the current conditions and generate and upload
HTML and graphs to [http://weather.wezm.net/][weather]. wview seemed like the
perfect tool for the job but unfortunately it proved unreliable, even with the
built in process monitoring. After a few hours of running fine it appeared the
HTML generation process would hang, preventing any further updates to the
website.
[install]: /personal/2010/09/weather-station/
[weather]: http://weather.wezm.net/
wview has a lot of functionality and thus a lot of code, which turned me
off trying to track down the bug. I looked around for
alternatives and found [Open2300][open2300], which is a core collection of
functions for communicating with a LaCrosse WS-23xx weather station and a set
of tools. wview logged its observations to an SQLite database, which seemed
appropriate for this application. There were tools in Open2300 to log current
conditions to MySQL and PostgreSQL databases but not SQLite. So last weekend
I spend some of the afternoon learning enough of the SQLite C API to add
such a tool. The result is in my
[git mirror of the Open2300 SVN repo][open2300git]. Once the SQLite tool is
tidied up a bit more I'll submit it upstream.
[open2300]: http://www.lavrsen.dk/foswiki/bin/view/Open2300/WebHome
[open2300git]: http://github.com/wezm/open2300
Now I had a database of observations I needed to replace the HTML pages
that wview was previously generated. To do this I wrote a small
[Lua tool][weather-tools]
to query the database and output the results to a [JSON][json] file. The
JSON is used on the new [weather page][weather], which is largely populated
by Javascript and uses the [flot charting library][flot] to graph the
temperature history.
[json]: http://www.json.org/
[flot]: http://code.google.com/p/flot/
[weather-tools]: http://github.com/wezm/weather-tools

View file

@ -0,0 +1,13 @@
---
title: Weather Station Software
extra: After trouble with the wview weather station software I built my own solution to log and publish weather conditions.
kind: article
section: technical
created_at: 2010-09-26 17:01:00
keywords:
- weather
- station
- wview
- open2300
- software
short_url: http://bit.ly/9Sf8Lu

15
content/weather.html Normal file
View file

@ -0,0 +1,15 @@
<h2>Temperature</h2>
<div class="loading"><img src="/images/spinner.gif" width="64" height="64" alt="Loading" /> Loading</div>
<div class="temperature chart">
<!--><button id="year">Year</button>
<button id="month">Month</button>
<button id="day">Day</button>-->
<div class="canvas"></div>
</div>
<!-- <h2>Rainfall</h2>
<div class="rainfall chart"></div> -->
<small>Forecast icons by <a href="http://lavana.deviantart.com/art/Flat-Weather-Icons-32021664">LavAna</a></small>

2
content/weather.yaml Normal file
View file

@ -0,0 +1,2 @@
---
title: Macedon Weather

18
layouts/weather.html Normal file
View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<%= render '_head' %>
<script src="/js/jquery.flot.min.js" type="text/javascript" charset="utf-8"></script>
<!--[if IE lt 9]><script language="javascript" type="text/javascript" src="/js/excanvas.min.js"></script><![endif]-->
<script src="/js/mojo.js" type="text/javascript" charset="utf-8"></script>
<script src="/js/weather.js" type="text/javascript" charset="utf-8"></script>
</head>
<body class="weather">
<%= render '_header' %>
<div id="content">
<h1><a href="<%= @item.reps.first.path %>"><%=h @item[:title] %></a></h1>
<%= yield %>
</div>
<%= render '_footer' %>
</body>
</html>

BIN
output/images/spinner.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

1
output/js/excanvas.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
output/js/jquery.flot.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -6,7 +6,7 @@
// --- Version
version: '0.2.0',
version: '0.3.0',
/**
* Escape HTML.
@ -33,8 +33,11 @@
* @api public
*/
normalize: function(object) {
return typeof object == 'function' ? object() : object
normalize: function(object, property) {
if(property === undefined)
return typeof object == 'function' ? object() : object
else
return typeof object[property] == 'function' ? object[property]() : object[property]
},
/**

154
output/js/weather.js Normal file
View file

@ -0,0 +1,154 @@
function render_current(o) {
return '<div id="current">\n\
<figure class="forecast">\n\
<img src="/images/weather/' + (Mojo.escape(Mojo.normalize(o, "forecast"))) + '.png" width="48" height="48" alt="' + (Mojo.escape(Mojo.normalize(o, "forecast"))) + '" />\n\
<figcaption>Forecast</figcaption>\n\
</figure>\n\
\n\
<div class="temperature">\n\
<div class="current temperature">' + (Mojo.escape(Mojo.normalize(o, "temperature"))) + '&deg;C</div>\n\
<div class="minmax">\n\
Minimum <span class="min temperature">' + (Mojo.escape(Mojo.normalize(o, "min_temp"))) + '&deg;C</span> at\n\
<time datetime="' + (Mojo.escape(Mojo.normalize(o, "min_datetime"))) + '">' + (Mojo.escape(Mojo.normalize(o, "minDateString"))) + '</time>\n\
Maximum <span class="max temperature">' + (Mojo.escape(Mojo.normalize(o, "max_temp"))) + '&deg;C</span> at\n\
<time datetime="' + (Mojo.escape(Mojo.normalize(o, "max_datetime"))) + '">' + (Mojo.escape(Mojo.normalize(o, "maxDateString"))) + '</time>\n\
</div>\n\
</div>\n\
</div>';
};
// {"wind_angle":270,"rel_humidity_in":51,"rain_1h":0,"temperature_out":9.9,"forecast":"Sunny","rain_24h":0,"dewpoint":7.11,"wind_chill":9.9,"temperature_in":20.8,"rel_humidity_out":83,"tendency":"Rising","wind_speed":0,"rel_pressure":970.7,"rain_total":1.55,"datetime":"2010-09-20 11:30:13","wind_direction":"W"}
// forecaset is Rainy, Cloudy or Sunny
// Return a string of a number padded with leading zeros
function padNumber(n, count) {
if(count === undefined) count = 2;
var string = n.toString();
var padding = [];
for(var i = count - string.length; i > 0; i--) {
padding.push('0');
}
return padding.join('') + string;
}
function isoString(date) {
// YYYY-MM-DDTHH:MM:SS
if(!date) return '';
return [
[
date.getUTCFullYear(),
padNumber(date.getUTCMonth() + 1),
padNumber(date.getUTCDate())
].join('-'),
'T',
[
padNumber(date.getUTCHours()),
padNumber(date.getUTCMinutes()),
padNumber(date.getUTCSeconds())
].join(':')
].join('')
}
function datetimeString(date) {
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
return [
date.getUTCDate(),
months[date.getUTCMonth()],
timeString(date)
].join(' ')
}
function timeString(date) {
return [
padNumber(date.getUTCHours()),
padNumber(date.getUTCMinutes())
].join(':')
}
jQuery(function() {
jQuery.getJSON("/weather.json", function(data, status) {
var count = data.history.length;
for(var i = 0; i < count; i++) {
data.history[i][0] = new Date(data.history[i][0]);
}
// Populate the current conditions
var current = {
temperature: data.current.temperature_out,
timestamp: function() {
var d = new Date(data.current.timestamp);
return datetimeString(d);
},
min_temp: data.current.min.temperature,
min_date: new Date(data.current.min.timestamp),
min_datetime: function() {
return isoString(this.min_date)
},
minDateString: function() {
return timeString(current.min_date)
},
max_temp: data.current.max.temperature,
max_date: new Date(data.current.max.timestamp),
max_datetime: function() {
return isoString(this.max_date)
},
maxDateString: function() {
return timeString(current.max_date)
},
forecast: data.current.forecast
};
var current_div = render_current(current);
$('.loading').replaceWith(current_div)
// Populate the charts
$('.temperature.chart .canvas').each(function() {
var self = this;
var options = {
xaxis: {
mode: "time"
}
};
jQuery.plot(self, data.history, options);
/*
$("#year").click(function () {
$.plot(self, data.history, {
xaxis: {
mode: "time",
minTickSize: [1, "month"] //,
// min: (new Date("1990/01/01")).getTime(),
// max: (new Date()).getTime()
}
});
});
$("#month").click(function () {
$.plot(self, data.history, {
xaxis: {
mode: "time",
min: (new Date("2010/08/21")).getTime(),
max: (new Date()).getTime()
}
});
});
$("#day").click(function () {
$.plot(self, data.history, {
xaxis: {
mode: "time",
min: (new Date("2010/09/21 00:00")).getTime(),
max: (new Date()).getTime()
}
});
});
*/
});
});
});

View file

@ -0,0 +1,16 @@
<div id="current">
<figure class="forecast">
<img src="/images/weather/{{forecast}}.png" width="48" height="48" alt="{{forecast}}" />
<figcaption>Forecast</figcaption>
</figure>
<div class="temperature">
<div class="current temperature">{{temperature}}&deg;C</div>
<div class="minmax">
Minimum <span class="min temperature">{{min_temp}}&deg;C</span> at
<time datetime="{{min_datetime}}">{{minDateString}}</time>
Maximum <span class="max temperature">{{max_temp}}&deg;C</span> at
<time datetime="{{max_datetime}}">{{maxDateString}}</time>
</div>
</div>
</div>