Skip to content

Instantly share code, notes, and snippets.

@pingswept
Created December 19, 2012 16:00
Show Gist options
  • Select an option

  • Save pingswept/4337794 to your computer and use it in GitHub Desktop.

Select an option

Save pingswept/4337794 to your computer and use it in GitHub Desktop.
Hacked first version of weather station code. Bloated, but works.
import sqlite3
import datetime
sql_live = """SELECT
strftime('%Y-%m-%d %H:%M GMT', logdate) AS logdate,
avg({0}) AS {0}
FROM log
WHERE logdate >= datetime('now', '-180 minutes')
GROUP BY strftime('%Y-%m-%d %H:%M', logdate)
ORDER BY 1;"""
sql_pastday = """SELECT
strftime('%Y-%m-%d %H:%M GMT', min(logdate)) AS logdate,
avg({0}) AS {0}
FROM log
WHERE logdate >= datetime('now', '-24 hours')
GROUP BY strftime('%Y-%m-%d %H', logdate), strftime('%M', logdate) / 10
ORDER BY 1;"""
sql_pastweek = """SELECT
strftime('%Y-%m-%d %H:%M GMT', logdate) AS logdate,
{0}
FROM log_byhour
WHERE logdate >= datetime('now', '-7 days')
ORDER BY 1;"""
sql_pastyear = """SELECT
strftime('%Y-%m-%d 00:00:00 GMT', logdate) AS logdate,
avg({0}) AS {0}
FROM log_byhour
GROUP BY strftime('%Y-%m-%d', logdate)
HAVING logdate < date('now')
ORDER BY 1;"""
band_pastyear = """SELECT
min({0}) AS min,
max({0}) AS max
FROM log_byhour
GROUP BY strftime('%Y-%m-%d', logdate)
HAVING logdate < date('now')
ORDER BY logdate;"""
sql_update = """INSERT INTO log_byhour
(logdate, temperature, humidity, pressure)
SELECT
strftime('%Y-%m-%d %H:00:00', logdate) AS logdate,
avg(temperature) AS temperature,
avg(humidity) AS humidity,
avg(pressure) AS pressure
FROM log
WHERE logdate >= ? AND logdate < ?
GROUP BY strftime('%Y-%m-%d %H', logdate)
ORDER BY 1;"""
def log(temperature, humidity, pressure):
logdate = datetime.datetime.utcnow()
try:
con = sqlite3.connect('/var/log/datalog.db')
with con:
cur = con.cursor()
cur.execute('INSERT INTO log (logdate, temperature, humidity, pressure) VALUES(?, ?, ?, ?)', (logdate, temperature, humidity, pressure))
except Exception, e:
print '## datalogger.log ## {0}'.format(e)
def getlog(period):
import json
data = {}
if period == 'pastyear':
sql = sql_pastyear
band = band_pastyear
elif period == 'pastweek':
sql = sql_pastweek
band = ''
elif period == 'pastday':
sql = sql_pastday
band = ''
else:
sql = sql_live
band = ''
con = sqlite3.connect('/var/log/datalog.db', detect_types=sqlite3.PARSE_DECLTYPES)
with con:
c = con.cursor()
for val in ['temperature', 'humidity', 'pressure']:
c.execute(sql.format(val))
data[val] = c.fetchall()
if band != '':
c.execute(band.format(val))
data[val + '_band'] = c.fetchall()
return json.dumps(data)
def update_byhour():
con = sqlite3.connect('/var/log/datalog.db', detect_types=sqlite3.PARSE_DECLTYPES)
with con:
c = con.cursor()
# Get startDate for incremental update
c.execute('SELECT MAX(logdate) FROM log_byhour;')
startDate = c.fetchone()[0]
if startDate == None:
# Rascal24 arrival in UK
startDate = datetime.datetime(2011, 12, 30)
else:
startDate = datetime.datetime.strptime(startDate, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(hours = 1)
# Get endDate
c.execute("SELECT strftime('%Y-%m-%d %H:00:00','now');")
endDate = c.fetchone()[0]
# Update log_byhour
for val in ['temperature', 'humidity', 'pressure']:
c.execute(sql_update.format(val), (startDate, endDate))
rows = c.rowcount
if rows > 0:
c.execute("DELETE FROM log WHERE logdate < datetime('now', '-36 hours');")
# print '## update_byhour ## deleted ' + str(c.rowcount)
return rows
def init(confirm):
if confirm:
logdate = datetime.datetime.utcnow()
temperature = 21.2
humidity = 56
pressure = 29.375
con = sqlite3.connect('/var/log/datalog.db')
with con:
cur = con.cursor()
cur.execute('DROP TABLE IF EXISTS log;')
cur.execute('CREATE TABLE log(logdate TIMESTAMP, temperature REAL, humidity REAL, pressure REAL);')
cur.execute('INSERT INTO log (logdate, temperature, humidity, pressure) VALUES(?, ?, ?, ?)', (logdate, temperature, humidity, pressure))
# Read back row to check, then delete
con = sqlite3.connect('/var/log/datalog.db', detect_types=sqlite3.PARSE_DECLTYPES)
con.row_factory = sqlite3.Row
with con:
c = con.cursor()
c.execute('select * from log;')
r = c.fetchone()
print 'Logdate: ' + r['logdate'].strftime('%a, %d %b %Y %H:%M %Z')
print 'Temperature: {0}'.format(r['temperature'])
print 'Humidity: {0}'.format(r['humidity'])
print 'Pressure: {0}'.format(r['pressure'])
c.execute('delete from log;')
else:
print 'Set arg to True to initialise (deletes all data)'
def init_byhour(confirm):
if confirm:
logdate = datetime.datetime.utcnow()
temperature = 21.2
humidity = 56
pressure = 29.375
con = sqlite3.connect('/var/log/datalog.db')
with con:
cur = con.cursor()
cur.execute('DROP TABLE IF EXISTS log_byhour;')
cur.execute('CREATE TABLE log_byhour(logdate TIMESTAMP, temperature REAL, humidity REAL, pressure REAL);')
cur.execute('INSERT INTO log_byhour (logdate, temperature, humidity, pressure) VALUES(?, ?, ?, ?)', (logdate, temperature, humidity, pressure))
# Read back row to check, then delete
con = sqlite3.connect('/var/log/datalog.db', detect_types=sqlite3.PARSE_DECLTYPES)
con.row_factory = sqlite3.Row
with con:
c = con.cursor()
c.execute('select * from log_byhour;')
r = c.fetchone()
print 'Logdate: ' + r['logdate'].strftime('%a, %d %b %Y %H:%M %Z')
print 'Temperature: {0}'.format(r['temperature'])
print 'Humidity: {0}'.format(r['humidity'])
print 'Pressure: {0}'.format(r['pressure'])
c.execute('delete from log_byhour;')
else:
print 'Set arg to True to initialise (deletes all data)'
# datalogger stuff
@rbtimer(30)
def log_value(num):
import datalogger
# artemp = pytronics.i2cRead(0x48, 0, 'I', 2)
# ftemp = ((artemp[0] << 4) | (artemp[1] >> 4)) * 0.0625
readings = pytronics.readWeatherBoard()
# print '## temp_log ## ' + str(ftemp)
temperature = (float(readings['temperature']) - 32.0) * 0.5555
humidity = readings['humidity']
pressure = readings['pressure']
datalogger.log(temperature, humidity, pressure)
@cron(-30, -1, -1, -1, -1)
def update_byhour(num):
import datalogger
rows = datalogger.update_byhour()
if rows != 0:
print '## updatelog ## added {0} row(s)'.format(rows)
@public.route('/datalogger.html')
def datalogger():
return render_template('datalogger.html', title='Temperature Log', label0='Temp {0}C'.format(unichr(176)))
@public.route('/weather-station.html')
def weather_station():
label0 = 'Temp {0}C'.format(unichr(176))
label1 = 'Humidity [%]'
label2 = 'Pressure [in Hg]'
return render_template('weather-station.html', title='Weather Log', label0=label0, label1=label1, label2=label2)
@public.route('/getlog', methods=['POST'])
def getlog():
import datalogger
try:
period = request.form['period']
except KeyError:
period = 'live'
return datalogger.getlog(period)
<!DOCTYPE html>
<html lang="en">
<head>
<title>Weather station demo</title>
{% include "include/rascal-head.html" %}
<script type="text/javascript" src="/static/js/jqplot-plugins/jqplot.dateAxisRenderer.js"></script>
<script type="text/javascript" src="/static/js/jqplot-plugins/jqplot.highlighter.js"></script>
<style>
#temperature-chart, #humidity-chart, #pressure-chart {
margin-top: 20px;
height: 400px;
width: 800px;
}
.jqplot-title, .jqplot-axis, .status {
color: #ddd;
}
.jqplot-table-legend-label {
color: #111;
}
.btn-group {
margin-top: 10px;
}
img.led {
opacity: 0.4;
}
img.led.active {
opacity: 1;
}
.close {
opacity: 0.6;
}
.close:hover {
opacity: 1;
}
</style>
</head>
<body>
{% include "include/rascal-topbar.html" %}
<div class="container">
<div class="well rascal">
<h1>{{ title }}</h1>
<div id="temperature-chart"></div>
<div id="humidity-chart"></div>
<div id="pressure-chart"></div>
<div class="row-fluid">
<div class="span8">
<div class="btn-group" data-toggle="buttons-radio">
<button value="live" class="btn btn-default rascal active" style="width: 120px;">Live</button>
<button value="pastday" class="btn btn-default rascal" style="width: 120px;">Past Day</button>
<button value="pastweek" class="btn btn-default rascal" style="width: 120px;">Past Week</button>
<button value="pastyear" class="btn btn-default rascal" style="width: 120px;">Past Year</button>
</div>
<img id="LED" src="static/images/led.gif" width="10" class="led" />
<small id="status" class="status"></small>
</div>
<div class="span4">
<div class="btn-group">
<button id="capture" value="capture" class="btn btn-default rascal" style="float: right;">Capture Image</button>
</div>
</div>
</div> <!-- /row-fluid -->
</div> <!-- /well -->
<div id="captured-image" class="well rascal alert fade in" style="display:none;">
<!-- <button type="button" class="close"><img src="/editor/static/images/file-icons/delete.png"></button> -->
<img class="close" src="/editor/static/images/file-icons/delete.png">
<p><small class="status">Right-click chart and choose Save Image As...</small></p>
<div id="img-container"></div>
</div> <!-- /well -->
</div> <!-- /container -->
<!-- {% include "include/doc-tab.html" %} -->
<script type="text/javascript">
var temperatureChartOptions = {
legend: {
show: true,
location: "ne"
},
series: [
{ label: "{{ label0 }}", lineWidth: 3, showMarker: false, rendererOptions: { smooth: true } }
],
axes: {
xaxis: {
renderer: $.jqplot.DateAxisRenderer,
tickOptions: {formatString: '%a%n%H:%M'}
},
yaxis: {
min: 13.0,
max: 25.0
// tickOptions: {formatString: '%0.2f'}
}
},
highlighter: {
show: true,
sizeAdjust: 7.5
},
seriesColors: [ "#cd2820" ]
};
var humidityChartOptions = {
legend: {
show: true,
location: "ne"
},
series: [
{ label: "{{ label1 }}", lineWidth: 3, showMarker: false, rendererOptions: { smooth: true } }
],
axes: {
xaxis: {
renderer: $.jqplot.DateAxisRenderer,
tickOptions: {formatString: '%a%n%H:%M'}
},
yaxis: {
min: 0.0,
max: 100.0
// tickOptions: {formatString: '%0.2f'}
}
},
highlighter: {
show: true,
sizeAdjust: 7.5
},
seriesColors: [ "#51a8cb" ]
};
var pressureChartOptions = {
legend: {
show: true,
location: "ne"
},
series: [
{ label: "{{ label2 }}", lineWidth: 3, showMarker: false, rendererOptions: { smooth: true } }
],
axes: {
xaxis: {
renderer: $.jqplot.DateAxisRenderer,
tickOptions: {formatString: '%a%n%H:%M'}
},
yaxis: {
min: 25.0,
max: 32.0
// tickOptions: {formatString: '%0.2f'}
}
},
highlighter: {
show: true,
sizeAdjust: 7.5
},
seriesColors: [ "#414243" ]
};
var
period = 'live',
interval = { live: 30*1000, pastday: 5*30*1000, pastweek: 30*60*1000, pastyear: 30*60*1000 },
tickFormat = { live: '%a%n%H:%M', pastday: '%a%n%H:%M', pastweek: '%a%n%H:%M', pastyear: '%e %b%n%Y' },
int_timer,
capture_timeout,
plot1,
plot2,
plot3;
function updateGraph() {
$('#LED').addClass('active');
$.post("/getlog", { period: period }, function (response) {
var data = $.parseJSON(response),
temperature = data.temperature,
humidity = data.humidity,
pressure = data.pressure,
temperature_band = data.temperature_band,
humidity_band = data.humidity_band,
pressure_band = data.pressure_band;
console.log(response);
console.log('updateGraph last data ' + temperature[temperature.length - 1][0]);
if (temperature_band !== undefined) {
temperatureChartOptions.temperature[0].rendererOptions.bandData = temperature_band;
}
//} else {
// delete temperatureChartOptions.temperature[0].rendererOptions.bandData;
//}
$('#temperature-chart').empty();
$('#humidity-chart').empty();
$('#pressure-chart').empty();
plot1 = $.jqplot('temperature-chart', [temperature], temperatureChartOptions);
plot2 = $.jqplot('humidity-chart', [humidity], humidityChartOptions);
plot3 = $.jqplot('pressure-chart', [pressure], pressureChartOptions);
$('#status').text('Last update ' + new Date());
setTimeout(function () {
$('#LED').removeClass('active');
}, 500);
}).error(function (jqXHR, textStatus, errorThrown) {
$('#status').text('Update failed (' + errorThrown + ') ' + new Date());
console.log('updateGraph: getlog: ' + textStatus + ': ' + errorThrown);
});
}
function captureImage() {
var options = {
x_offset: 0,
y_offset: 0,
backgroundColor: $('.well.rascal').css('background-color')
},
imgElem = $('#temperature-chart').jqplotToImageElem(options);
$('#img-container')
.empty()
.append(imgElem);
$("#captured-image").fadeTo(250, 1);
}
// If update in progress, wait until finished (max 10s)
function captureIfYouCan() {
var isBusy = $('#LED').hasClass('active');
if (!isBusy) {
captureImage();
} else {
if (capture_timeout < 20) {
capture_timeout += 1;
console.log('Capture Image: waiting for update ' + capture_timeout);
setTimeout(captureIfYouCan, 500);
} else {
console.log('Capture Image: timed out waiting for update');
}
}
}
$('.btn').click(function () {
var which = $(this).attr('value');
if (which === 'capture') {
capture_timeout = 0;
captureIfYouCan();
} else {
int_timer = clearInterval(int_timer);
period = which;
console.log('Period set to ' + period + ' (refresh ' + (interval[period] / 1000).toString() + 's)');
temperatureChartOptions.axes.xaxis.tickOptions.formatString = tickFormat[period];
humidityChartOptions.axes.xaxis.tickOptions.formatString = tickFormat[period];
pressureChartOptions.axes.xaxis.tickOptions.formatString = tickFormat[period];
console.log(temperatureChartOptions.axes.xaxis.tickOptions.formatString);
console.log(humidityChartOptions.axes.xaxis.tickOptions.formatString);
console.log(pressureChartOptions.axes.xaxis.tickOptions.formatString);
updateGraph();
int_timer = setInterval(updateGraph, interval[period]);
}
});
$(document).ready(function () {
"use strict";
period = 'live'
updateGraph();
int_timer = setInterval(updateGraph, interval[period]);
$('.alert .close').live("click", function(e) {
$(this).parent().fadeTo(350, 0, function () {
$(this).hide();
});
});
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment