//
// Script to update the 'now' conditions web page
//
// M Crossley 26 Nov 2013, 31 Aug 2014, 02 Sep 2014
//
// JSHint options:
/*global Highcharts*/
/*jshint jquery:true, plusplus:false*/
//
// Main name space, isolate us from the global namespace
//
var weather;
weather = (function () {
'use strict';
var lastVals = {}; //stores all the last values to see if they have changed.
var cumulus = {}; //stores all the data from cumulus
var update = false; //initial load or update?
var pageUpdate = 10; //page update interval in seconds
var pageCountdown = pageUpdate; //page update countdown timer
var imgRefresh = 600; //how often to refresh the Sun/Moon graphics: 10 mins = 600 secs
var imgCountdown = Date.now() + imgRefresh * 1000; //image update countdown timer
var httpError = 0;
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var chart;
var options;
var numDisplayRecs = 720; //720 = 12 hours at 1 minute per record
var wasSunny = false; //was it Sunny the last time we got data?
var sunPlotBandIdx = 0; //index to latest sun plotband
var wasNight = false; //was it night the last rime we got data?
var nightPlotBandIdx = 0; //index to latest night plotband
var jqXHR = {};
function setStatus(str) {
$('#status').html(str);
}
function countDown() {
var strCnt;
pageCountdown--;
if (pageCountdown <= 0) {
getData();
pageCountdown = pageUpdate;
} else {
setTimeout(countDown, 970); // knock a bit off 1 second to allow for script processing time
strCnt = ('0' + pageCountdown).slice(-2);
if (httpError === 0) {
setStatus('Next update ' + strCnt + ' secs');
} else {
setStatus('HTTP request failed, error:' + httpError + ' Retry:' + strCnt + 's');
}
}
if (imgCountdown <= Date.now()) {
$('#img_sun').attr('src', 'http://weather.wilmslowastro.com/images ... shine=true&_=' + Date.now());
$('#img_moon').attr('src', 'http://weather.wilmslowastro.com/utils/ ... hp?size=75&_=' + Date.now());
//$('#sager_forecast').text($.ajax({
// type: 'GET',
// url: 'utils/sager.php?print',
// cache: false,
// async: false
// }).responseText);
// Update the WU Current Conds data
$.ajax({
url: 'utils/getWuCurrentConds.php',
datatype: 'json',
cache: false,
timeout: 10 * 1000,
success: function (data) {
$('#wuCurrCond').text(data.current_observation.weather);
$('#wuCurrCondImg').attr('src', data.current_observation.icon_url);
$('#wuCurrCondImg').attr('alt', data.current_observation.weather);
}
});
// Update the WU Forecast data
$.ajax({
url: 'utils/getWuForecast.php',
datatype: 'json',
cache: false,
timeout: 10 * 1000,
success: function (data) {
var i, nr, day;
for (i=0; i < data.forecast.simpleforecast.forecastday.length; i++) {
day = data.forecast.simpleforecast.forecastday
;
nr = '<tr id="wuFC' + i + '"><td>' + day.date.weekday_short + '</td>';
nr += '<td>' + day.low.celsius + '°C</td><td>' + day.high.celsius + '°C</td>';
nr += '<td class="only-wide">' + day.conditions + '</td>';
nr += '<td><img src="' + day.icon_url + '" class="icon" alt="icon" title="' + day.conditions + '"/></td></tr>';
$('tr#wuFC'+i).replaceWith(nr);
}
}
});
imgCountdown = Date.now() + imgRefresh * 1000;
}
}
function doUpdate() {
var last, id, val, numLast, numVal;
//get all the spans on the page
$('span').each(function () {
id = this.id ? this.id : this.className;
val = cumulus[id]; //get the cumulus value
if (val !== undefined) { //do we have a cumulus value for this field?
last = lastVals[id]; //do we have a lastVal?
this.innerHTML = val; //set the field text
if (last === val) { //not updated
this.style.color = ''; //clear any red flag
} else {
if (update && !(id === 'time' || id === 'LastDataReadT')) { //not initial page load, or the time field
this.style.color = 'darkGreen'; //flag general change
if (id !== 'avgbearing') { //not the bearing figure - it wraps 0-360 - too complex!
numLast = parseFloat(last);
numVal = parseFloat(val);
if (numVal > numLast) {
this.style.color = 'red'; //flag red
this.innerHTML = '\u2191' + this.innerHTML; //add up arrow
} else if (numVal < numLast) {
this.style.color = 'blue'; //flag blue
this.innerHTML = '\u2193' + this.innerHTML; //add down arrow
}
}
}
lastVals[id] = val; //store new val as last
}
}
});
}
function checkDataResp() {
var str = cumulus.LastRainTipISO.split(' '),
dt = str[0].split('-'),
tm = str[1].split(':'),
today = new Date(),
then = new Date(),
thenPlus1 = new Date(),
shift, tim,
status = $('#led');
today.setHours(0, 0, 0, 0);
then.setFullYear(dt[0], dt[1] - 1, dt[2]);
then.setHours(tm[0], tm[1], 0, 0);
thenPlus1.setTime(then.getTime() + 86400000);
if (then.getTime() >= today.getTime()) {
cumulus.LastRainTipISO = 'Today ';
// do not at 'at ' if we are in narrow mode (tablets)
cumulus.LastRainTipISO += skel.vars.stateId.indexOf('narrow') >= 0 ? '' : 'at ';
} else if (thenPlus1.getTime() >= today.getTime()) {
cumulus.LastRainTipISO = 'Yesterday ';
// do not at 'at ' if we are in narrow mode (tablets)
cumulus.LastRainTipISO += skel.vars.stateId.indexOf('narrow') >= 0? '' : 'at ';
} else {
cumulus.LastRainTipISO = '' + then.getDate() + ' ' + months[then.getMonth()] + ' ';
}
cumulus.LastRainTipISO += str[1];
/*
if (+cumulus.DataStopped === 1) {
status.attr({
src: 'images/red-on-16.png',
alt: 'Remote sensor contact lost',
title: 'Remote sensor contact lost'
});
} else {
if (status.attr('src') !== 'images/green-on-16.png') {
status.attr({
src: 'images/green-on-16.png',
alt: 'Remote sensor OK',
title: 'Remote sensor OK'
});
}
}
*/
cumulus.DataStopped = +cumulus.DataStopped === 0 ? 'ok' : 'stopped';
cumulus.battery = cumulus.battery.toUpperCase();
cumulus.press = (+cumulus.press).toFixed(1);
cumulus.pressTL = (+cumulus.pressTL).toFixed(1);
cumulus.pressTH = (+cumulus.pressTH).toFixed(1);
cumulus.presstrendval = (+cumulus.presstrendval).toFixed(1);
cumulus.batteries = 'Console-' + (parseFloat(cumulus.battery) > 3.75 ? 'ok' : 'LOW') + ', ' +
'ISS-' + cumulus.txbattery2 + ', ' +
'Wind-' + cumulus.txbattery1;
if (cumulus.txbattery1 !== 'ok' || cumulus.txbattery2 !== 'ok') {
if (status.attr('src') === 'images/green-on-16.png') {
status.attr({
src: 'images/red-on-16.png',
alt: 'Remote transmitter battery low',
title: 'Remote transmitter battery low'
});
} else {
status.attr({
src: 'images/red-on-16.png',
alt: status.attr('alt') + ', battery low',
title: status.attr('alt') + ', battery low'
});
}
}
if (+cumulus.rfall >= 0.2) {
cumulus.ConsecutiveRainDays = (+cumulus.ConsecutiveRainDays + 1).toString();
if (skel.vars.stateId.indexOf('narrow') >= 0) {
$('#dryDaysTitle').text('Prev');
$('#wetDaysTitle').text('Run of');
} else {
$('#dryDaysTitle').text('Previous');
$('#wetDaysTitle').text('Consecutive');
}
} else {
if (skel.vars.stateId.indexOf('narrow') >= 0) {
$('#dryDaysTitle').text('Run of');
$('#wetDaysTitle').text('Prev');
} else {
$('#dryDaysTitle').text('Consecutive');
$('#wetDaysTitle').text('Previous');
}
}
if (cumulus.rrate > 50.0) {
cumulus.rrateText = '(extreme rain!)';
} else if (cumulus.rrate >= 16.0) {
cumulus.rrateText = '(very heavy rain)';
} else if (cumulus.rrate >= 4.0) {
cumulus.rrateText = '(heavy rain)';
} else if (cumulus.rrate >= 1.0) {
cumulus.rrateText = '(moderate rain)';
} else if (cumulus.rrate >= 0.25) {
cumulus.rrateText = '(light rain)';
} else if (cumulus.rrate > 0.0) {
cumulus.rrateText = '(very light rain)';
} else {
cumulus.rrateText = '';
}
cumulus.Sunny = (cumulus.IsSunUp === '1' ? (cumulus.IsSunny === '1' ? 'Yes' : 'No') : 'Sun set');
cumulus.ET = (+cumulus.ET < 0 ? '0.00' : cumulus.ET);
/*
if (!update) {
// only update the day/night chart on inital load
dayPct = Math.round((+cumulus.daylength.split(':')[0] + cumulus.daylength.split(':')[1] / 60) / 24 * 1000) / 10;
duskPct = Math.round((+cumulus.daylightlength.split(':')[0] + cumulus.daylightlength.split(':')[1] / 60) / 24 * 1000) / 10;
duskPct -= dayPct;
nightPct = 100 - dayPct - duskPct;
duskPct = Math.round(duskPct / 2 * 10) / 10;
cumulus.daylightChart = '<img src='http://chart.apis.google.com/chart?' +
'chs=75x75' +
'&chtt=day/night' +
'&chts=000000,12' +
'&chma=0,0,0,0' +
'&chd=t:' + duskPct + ',' + dayPct + ',' + duskPct + ',' + nightPct +
'&cht=p3' +
'&chp=' + (Math.PI - (dayPct + duskPct * 2) / 100 * Math.PI) +
'&chf=bg,s,FFFFFF00' +
'&chco=FF6600,FFD700,FF6600,303060 ' +
'alt="day/night ratio"/>';
}
*/
//update the page
doUpdate();
//start the count for next update
countDown();
if (update) {
// add latest update to the graph
//tim = cumulus.timeUTC;
tim = Date.UTC(cumulus.timearray[0],+cumulus.timearray[1]-1,cumulus.timearray[2],cumulus.timearray[3],cumulus.timearray[4],0);
// do we need to remove an old records whilst adding the new to stop the graph growing?
//shift = chart.series[0].data.length < numDisplayRecs ? false : true;
//shift = true;
shift = (tim - chart.series[0].data[0].x) / 60000 > numDisplayRecs;
chart.series[0].addPoint([tim, +cumulus.rfall], false, shift);
chart.series[1].addPoint([tim, +cumulus.press], false, shift);
chart.series[2].addPoint([tim, +cumulus.wspeed], false, shift);
chart.series[3].addPoint([tim, +cumulus.temp], false, shift);
chart.series[4].addPoint([tim, +cumulus.dew], false, shift);
chart.series[5].addPoint([tim, +cumulus.wgust], false, shift);
chart.series[6].addPoint([tim, +cumulus.hum], false, shift);
//chart.series[7].addPoint([tim, +cumulus.IsSunny], false, shift);
// add IsSunny to the xAxis plot bands
if (wasSunny) {
// it was sunny when we last got data, so update the last plotband
//chart.xAxis[0].plotBands[chart.xAxis[0].plotBands.length - 1].to = tim; // HC >=3.0.9
chart.xAxis[0].plotLinesAndBands[sunPlotBandIdx].options.to = tim; // HC <=3.0.7
if (+cumulus.IsSunny === 0) {
// but the sun has gone now
wasSunny = false;
}
} else if (!wasSunny && +cumulus.IsSunny === 1) {
// it wasn't sunny when we last got data, but it has come out now.
// so start a new plotband
chart.xAxis[0].addPlotBand({
from: tim,
to: tim,
color: 'rgba(255,194,0,0.3)',
zIndex: 0
});
wasSunny = true;
sunPlotBandIdx = chart.xAxis[0].plotLinesAndBands.length - 1;
}
// add night-time to the xAxis plot bands
if (wasNight) {
// it was night when we last got data, so update the last plotBand
chart.xAxis[0].plotLinesAndBands[nightPlotBandIdx].options.to = tim;
if (+cumulus.IsSunUp === 1) {
// but sun is now up
wasNight = false;
}
} else if (!wasNight && +cumulus.IsSunUp === 0) {
// it wasn't night when we last got data, but it is now
// so start a new plot band
chart.xAxis[0].addPlotBand({
from: tim,
to: tim,
color: 'rgba(0,0,60,0.85)',
zIndex: 1
});
wasNight = true;
nightPlotBandIdx = chart.xAxis[0].plotLinesAndBands.length - 1;
}
chart.redraw();
} else {
update = true;
}
//time a page update to clear any fields flagged as updated - after 10 seconds
window.setTimeout(doUpdate, 10 * 1000);
}
function getData() {
setStatus('Downloading...');
if ($.active > 0) {
// kill any outstanding requests
jqXHR.abort();
}
/*
jqXHR = $.ajax({
//url: 'cumuluswebtags.json',
url: 'utils/getIndexPageData.php',
datatype: 'json',
cache: false,
timeout: 10 * 1000
}).done(function (data) {
cumulus = data;
checkDataResp();
}).fail(function (xhr, status, error) {
checkDataError(xhr);
});
*/
jqXHR = $.ajax({
//url: 'cumuluswebtags.json',
url: 'utils/getIndexPageData.php',
datatype: 'json',
cache: false,
timeout: 10 * 1000,
success: function (data) {
cumulus = data;
checkDataResp();
},
error: checkDataError
});
}
function checkDataError(xhr, status, error) {
if (status !== 'abort') {
pageCountdown = 6;
// setStatus('HTTP request failed, error: ' + xhr.status);
setStatus('HTTP request failed, error: ' + xhr.status + ' Retrying..');
// if ($.active === 0) {
setTimeout(getData, 5000); }
// }
}
$(document).ready(function () {
getData();
// get rid of the comma thousands separator
Highcharts.setOptions({ lang: { thousandsSep: '' } });
// define the options
options = {
chart: {
renderTo: 'container',
backgroundColor: '#F5F5F5'
},
credits: {
enabled: false
},
title: {
text: 'Rolling 12 hours',
y: 10,
margin: 20
},
subtitle: {
text: 'Click legend to enable/disable, background yellow: sun, blue: night',
y: 27
},
xAxis: {
type: 'datetime',
maxPadding: 0.005,
minPadding: 0.005,
dateTimeLabelFormats: {
millisecond: '%H:%M:%S',
second: '%H:%M:%S',
minute: '%H:%M',
hour: '%H:%M',
day: '%e %b'
}
},
yAxis: [{
// left y axis 0
title: {
text: 'pressure (hPa)',
style: { color: '#058DC7' },
margin: 5
},
minRange: 5,
showEmpty: false,
labels: {
formatter: function () {
return this.value.toFixed(0);
},
style: { color: '#058DC7' }
}
}, { // left y axis 1
title: {
text: 'rainfall (mm)',
style: { color: '#83D3FF' },
margin: 5
},
min: 0,
minRange: 5,
showEmpty: false,
labels: {
style: { color: '#83D3FF' }
}
}, { // right y axis 2
title: {
text: 'wind (mph)',
style: { color: '#50B432' },
margin: 5
},
opposite: true,
min: 0,
minRange: 3,
showEmpty: false,
labels: {
style: { color: '#50B432' }
}
}, { // right y axis 3
title: {
text: 'temperature (°C)',
style: { color: '#F00' },
margin: 5
},
opposite: true,
minRange: 2,
showEmpty: false,
plotLines: [{
// visualize zero
value: 0,
color: '#0000B4',
width: 1,
zIndex: 2
}],
labels: {
formatter: function () {
if (this.value <= 0) {
return '<span style="fill: blue;">' + this.value + '</span>';
} else {
return '<span style="fill: red;">' + this.value + '</span>';
}
}
//style: { color: '#F00' }
}
}, { // right y axis 4
title: {
text: 'relative humidity (%)',
style: { color: 'rgba(0,0,0,0.4)' },
margin: 5
},
opposite: true,
max : 100,
minRange: 20,
labels : {
//align : 'right',
//x : 5,
formatter : function () {
return this.value.toFixed(0);
},
style: { color: 'rgba(0,0,0,0.4)' }
},
showEmpty: false
}, { // left y axis 5 - Sunshine - Hidden
type: 'category',
categories: [0, 1],
linewidth: 0,
minorGridLineWidth: 0,
lineColor: 'transparent',
title: { enabled: false },
labels: { enabled: false },
minorTickLength: 0,
tickLength: 0,
min: 0,
max: 1,
showEmpty: false
}],
legend: {
verticalAlign: 'top',
y: 28,
borderWidth: 0
},
plotOptions: {
series: {
cursor: 'pointer',
marker: { enabled: false }
}
},
tooltip: {
xDateFormat: '%d/%m/%Y %H:%M'
},
series: [{
name: 'Rainfall',
type: 'area',
data: [],
yAxis: 1,
tooltip: { valueSuffix: ' mm' },
color: 'rgba(100,200,255,0.8)',
fillColor: {
linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1},
stops: [
[0, 'rgba(10,80,255,0.3)'],
[0.33, 'rgba(10,150,50,0.08)'],
[1, 'rgba(10,150,50,0.08)']
]
}
}, {
name: 'Pressure',
type: 'line',
data: [],
yAxis: 0,
tooltip: {
valueSuffix: ' hPa',
valueDecimals: 1
},
visible: true
}, {
name: 'Avg wind',
type: 'line',
step: true,
data: [],
yAxis: 2,
tooltip: { valueSuffix: ' mph' },
visible: false
}, {
name: 'Temperature',
type: 'line',
data: [],
yAxis: 3,
tooltip: { valueSuffix: ' °C' }
}, {
name: 'Dew Point',
type: 'line',
data: [],
yAxis: 3,
tooltip: { valueSuffix: ' °C' },
visible: false
}, {
name: 'Gust',
type: 'line',
step: true,
data: [],
yAxis: 2,
tooltip: { valueSuffix: ' mph' },
color: '#85BE0A'
}, {
name: 'Humidity',
type: 'line',
data: [],
yAxis: 4,
tooltip: { valueSuffix: '%' },
color: 'rgba(0,0,0,0.4)',
visible: false
}]
}; // end options{}
// draw the chart
chart = new Highcharts.Chart(options);
$.ajax({
url: 'utils/realtimeLogSql.php?recordAge=12&recordUnit=hour&rfall&press&wspeed&temp&dew&wgust&hum&IsSunny&IsSunUp',
datatype: 'json',
cache: false
success: function (resp) {
// hide some plots on initial load
// chart.series[1].visible = false;
// chart.series[2].visible = false;
// chart.series[4].visible = false;
// chart.series[6].visible = false;
chart.series[0].setData(resp.rfall);
chart.series[1].setData(resp.press);
chart.series[2].setData(resp.wspeed);
chart.series[3].setData(resp.temp);
chart.series[4].setData(resp.dew);
chart.series[5].setData(resp.wgust);
chart.series[6].setData(resp.hum);
//walk the sunshine data and create a set of plot bands
var sunOut = 0;
var plotBand = {};
for (var i=0; i<resp.IsSunny.length; i++) {
if (resp.IsSunny[1] === 1 && sunOut === 0) {
plotBand = {
color: 'rgba(255,194,0,0.3)',
from: resp.IsSunny[0],
zIndex: 0
};
sunOut = 1;
} else if (resp.IsSunny[1] === 0 && sunOut === 1) {
plotBand.to = resp.IsSunny[0];
chart.xAxis[0].addPlotBand(plotBand);
sunOut = 0;
sunPlotBandIdx++;
}
}
// trap if sun is out now
if (sunOut === 1) {
plotBand.to = resp.IsSunny[resp.IsSunny.length - 1][0];
chart.xAxis[0].addPlotBand(plotBand);
wasSunny = true;
}
//walk the IsSunUp data and create a set of plot bands
var sunUp = 1;
plotBand = {};
nightPlotBandIdx = sunPlotBandIdx;
for (i=0; i<resp.IsSunUp.length; i++) {
if (resp.IsSunUp[1] === 0 && sunUp === 1) {
plotBand = {
//color: 'rgba(104,57,155,0.2)',
color: 'rgba(0,0,60,0.85)',
// color: {
// linearGradient: {x1: 0, x2: 1, y1: 0, y2: 0},
// stops: [
// [0, 'rgba(255,0,0,0.2)'],
// [0.25, 'rgba(104,57,155,0.2)'],
// [0.75, 'rgba(104,57,155,0.2)'],
// [1, 'rgba(255,0,0,0.2)']
// ]
// },
from: resp.IsSunUp[0],
zIndex: 1
};
sunUp = 0;
} else if (resp.IsSunUp[1] === 1 && sunUp === 0) {
plotBand.to = resp.IsSunUp[0];
chart.xAxis[0].addPlotBand(plotBand);
sunUp = 1;
nightPlotBandIdx++;
}
}
// trap if sun is down now
if (sunUp === 0) {
plotBand.to = resp.IsSunUp[resp.IsSunUp.length - 1][0];
chart.xAxis[0].addPlotBand(plotBand);
wasNight = true;
}
}
}); // end ajax()
}); // end document ready()
$(function () {
$.ajaxSetup({
error: function (jqXHR) {
checkDataError(jqXHR.status);
}
});
});
}());