mirror of
https://github.com/wezm/wezm.net.git
synced 2024-12-18 18:29:54 +00:00
Merge branch 'weather'
This commit is contained in:
commit
cd35c78029
19 changed files with 325 additions and 5 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@ output/*/*/*/*/*.html
|
|||
output/fonts/*
|
||||
tmp/*
|
||||
.*.swp
|
||||
output/weather.json
|
||||
|
|
5
Rules
5
Rules
|
@ -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'
|
||||
|
|
|
@ -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/)
|
||||
|
|
|
@ -146,7 +146,7 @@ body > header
|
|||
|
||||
span
|
||||
display: none
|
||||
|
||||
|
||||
|
||||
#menu
|
||||
float: right
|
||||
|
@ -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
|
||||
|
|
37
content/technical/2010/09/weather-station-software.html
Normal file
37
content/technical/2010/09/weather-station-software.html
Normal 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
|
13
content/technical/2010/09/weather-station-software.yaml
Normal file
13
content/technical/2010/09/weather-station-software.yaml
Normal 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
15
content/weather.html
Normal 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
2
content/weather.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
title: Macedon Weather
|
18
layouts/weather.html
Normal file
18
layouts/weather.html
Normal 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
BIN
output/images/spinner.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
BIN
output/images/weather/Cloudy.png
Normal file
BIN
output/images/weather/Cloudy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3 KiB |
BIN
output/images/weather/Rainy.png
Normal file
BIN
output/images/weather/Rainy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
BIN
output/images/weather/Sunny.png
Normal file
BIN
output/images/weather/Sunny.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
1
output/js/dygraph-combined.js
Normal file
1
output/js/dygraph-combined.js
Normal file
File diff suppressed because one or more lines are too long
1
output/js/excanvas.min.js
vendored
Normal file
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
1
output/js/jquery.flot.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -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
154
output/js/weather.js
Normal 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"))) + '°C</div>\n\
|
||||
<div class="minmax">\n\
|
||||
Minimum <span class="min temperature">' + (Mojo.escape(Mojo.normalize(o, "min_temp"))) + '°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"))) + '°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()
|
||||
}
|
||||
});
|
||||
});
|
||||
*/
|
||||
});
|
||||
});
|
||||
});
|
16
templates/current_weather.html
Normal file
16
templates/current_weather.html
Normal 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}}°C</div>
|
||||
<div class="minmax">
|
||||
Minimum <span class="min temperature">{{min_temp}}°C</span> at
|
||||
<time datetime="{{min_datetime}}">{{minDateString}}</time>
|
||||
Maximum <span class="max temperature">{{max_temp}}°C</span> at
|
||||
<time datetime="{{max_datetime}}">{{maxDateString}}</time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in a new issue