New, updated version of web. Might contain customizations

This commit is contained in:
2019-08-21 15:44:49 +02:00
parent c3850b6e19
commit 47326ed774
11 changed files with 600 additions and 536 deletions

View File

@@ -1,59 +1,65 @@
#!/usr/bin/python3
"""Our cherrypy server, it all starts here
Let's look at the statically linked stuff as well (i.e. the files that will
serve us data). These imports are static files in the same directory. Each
a spearate application with it's own face and behavior
"""
import os
import cherrypy
import sys
# UI for weather ./weather.py
import weather
# CSV "API" for graphs, or individual pulling ./dynamic.py
import dynamic
# UI for RasPi / davis status ./status.py
import status
PATH = os.path.abspath(os.path.dirname(__file__))
import index
from modules import voltage
from modules import weather
from modules import dynamic
from modules import status
from modules import temphumi
import config
# Where are we?
_SCRIPT_PATH = os.path.dirname(sys.argv[0])
# Certificates fro SSL reside here
_CERT_PATH = _SCRIPT_PATH + '/.cert'
# So we can load static libraries
# sys.path.append(_SCRIPT_PATH)
def main_server_loop():
''' Master http server - the main executable / daemon
# basic config for the server, the SSL part is questionable...
server_config = {
'server.socket_host': '0.0.0.0',
'server.socket_port': 80
}
# commit the config settings
cherrypy.config.update(server_config)
Contains basic server settings and how the sub-modules
are called and mounted to their respective paths
# If launched directly, let's go
if __name__ == '__main__':
Args:
*None*
Sets:
*server_config:* dict(), updates cherrypy.config
*conf:* dict(), see Cherrypy docs for more
*cherrypy.config:* dict(), see Cherrypy docs for more
Returns:
*N/A*
Raises:
*Exception* If server is unable to start
'''
server_config={
'server.socket_host': config.Conf.val['_server_bind_ip'],
'server.socket_port': config.Conf.val['_server_port']
}
cherrypy.config.update(server_config)
conf = {
'/': {
'tools.sessions.on': True,
'tools.staticdir.root': os.path.abspath(_SCRIPT_PATH + '/')
'tools.staticdir.root': os.path.abspath(config.SCRIPT_PATH + '/')
},
'/static': {
'tools.staticdir.on': True,
'tools.staticdir.dir': os.path.abspath(_SCRIPT_PATH + '/static')
},
'/data': {
'tools.staticdir.on': False,
'tools.staticdir.dir': os.path.abspath(_SCRIPT_PATH + '/dynamic')
'tools.staticdir.dir': './static'
}
}
# Here are the different served mounts
cherrypy.tree.mount(weather.WeatherInfo(), "/", conf)
cherrypy.tree.mount(voltage.EnergyInfo(), "/", conf)
cherrypy.tree.mount(voltage.EnergyInfo(), "/energy", conf)
cherrypy.tree.mount(weather.WeatherInfo(), "/weather", conf)
cherrypy.tree.mount(status.StatusInfo(), "/status", conf)
cherrypy.tree.mount(dynamic.DynamicData(), "/data", conf)
# Run the server, lock it in place.
cherrypy.tree.mount(dynamic.Expose(), "/data", conf)
cherrypy.tree.mount(temphumi.PuerhInfo(), "/temphumi", conf)
cherrypy.engine.start()
cherrypy.engine.block()
if __name__ == '__main__':
try:
main_server_loop()
except Exception as e:
raise e

View File

@@ -10,35 +10,83 @@ var process = module.exports = {};
var cachedSetTimeout;
var cachedClearTimeout;
function defaultSetTimout() {
throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
throw new Error('clearTimeout has not been defined');
}
(function () {
try {
cachedSetTimeout = setTimeout;
} catch (e) {
cachedSetTimeout = function () {
throw new Error('setTimeout is not defined');
if (typeof setTimeout === 'function') {
cachedSetTimeout = setTimeout;
} else {
cachedSetTimeout = defaultSetTimout;
}
} catch (e) {
cachedSetTimeout = defaultSetTimout;
}
try {
cachedClearTimeout = clearTimeout;
} catch (e) {
cachedClearTimeout = function () {
throw new Error('clearTimeout is not defined');
if (typeof clearTimeout === 'function') {
cachedClearTimeout = clearTimeout;
} else {
cachedClearTimeout = defaultClearTimeout;
}
} catch (e) {
cachedClearTimeout = defaultClearTimeout;
}
} ())
function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0);
} else {
return cachedSetTimeout.call(null, fun, 0);
}
// if setTimeout wasn't available but was latter defined
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
cachedSetTimeout = setTimeout;
return setTimeout(fun, 0);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedSetTimeout(fun, 0);
} catch(e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedSetTimeout.call(null, fun, 0);
} catch(e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
return cachedSetTimeout.call(this, fun, 0);
}
}
}
function runClearTimeout(marker) {
if (cachedClearTimeout === clearTimeout) {
clearTimeout(marker);
} else {
cachedClearTimeout.call(null, marker);
//normal enviroments in sane situations
return clearTimeout(marker);
}
// if clearTimeout wasn't available but was latter defined
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
cachedClearTimeout = clearTimeout;
return clearTimeout(marker);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedClearTimeout(marker);
} catch (e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedClearTimeout.call(null, marker);
} catch (e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
return cachedClearTimeout.call(this, marker);
}
}
}
var queue = [];
var draining = false;
@@ -121,6 +169,10 @@ process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;
process.listeners = function (name) { return [] }
process.binding = function (name) {
throw new Error('process.binding is not supported');
@@ -2789,7 +2841,8 @@ DygraphInteraction.defaultModel = {
// Give plugins a chance to grab this event.
var e = {
canvasx: context.dragEndX,
canvasy: context.dragEndY
canvasy: context.dragEndY,
cancelable: true
};
if (g.cascadeEvents_('dblclick', e)) {
return;
@@ -3447,6 +3500,12 @@ if (typeof process !== 'undefined') {
"type": "integer",
"description": "Width, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored."
},
"pixelRatio": {
"default": "(devicePixelRatio / context.backingStoreRatio)",
"labels": ["Overall display"],
"type": "float",
"description": "Overrides the pixel ratio scaling factor for the canvas's 2d context. Ordinarily, this is set to the devicePixelRatio / (context.backingStoreRatio || 1), so on mobile devices, where the devicePixelRatio can be somewhere around 3, performance can be improved by overriding this value to something less precise, like 1, at the expense of resolution."
},
"interactionModel": {
"default": "...",
"labels": ["Interactive Elements"],
@@ -3716,7 +3775,7 @@ if (typeof process !== 'undefined') {
"default": "null",
"labels": ["Axis display", "Interactive Elements"],
"type": "float",
"description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% pased the edges of the displayed values. null means no bounds."
"description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% passed the edges of the displayed values. null means no bounds."
},
"title": {
"labels": ["Chart labels"],
@@ -4646,29 +4705,36 @@ var dateTicker = function dateTicker(a, b, pixels, opts, dygraph, vals) {
exports.dateTicker = dateTicker;
// Time granularity enumeration
var Granularity = {
SECONDLY: 0,
TWO_SECONDLY: 1,
FIVE_SECONDLY: 2,
TEN_SECONDLY: 3,
THIRTY_SECONDLY: 4,
MINUTELY: 5,
TWO_MINUTELY: 6,
FIVE_MINUTELY: 7,
TEN_MINUTELY: 8,
THIRTY_MINUTELY: 9,
HOURLY: 10,
TWO_HOURLY: 11,
SIX_HOURLY: 12,
DAILY: 13,
TWO_DAILY: 14,
WEEKLY: 15,
MONTHLY: 16,
QUARTERLY: 17,
BIANNUAL: 18,
ANNUAL: 19,
DECADAL: 20,
CENTENNIAL: 21,
NUM_GRANULARITIES: 22
MILLISECONDLY: 0,
TWO_MILLISECONDLY: 1,
FIVE_MILLISECONDLY: 2,
TEN_MILLISECONDLY: 3,
FIFTY_MILLISECONDLY: 4,
HUNDRED_MILLISECONDLY: 5,
FIVE_HUNDRED_MILLISECONDLY: 6,
SECONDLY: 7,
TWO_SECONDLY: 8,
FIVE_SECONDLY: 9,
TEN_SECONDLY: 10,
THIRTY_SECONDLY: 11,
MINUTELY: 12,
TWO_MINUTELY: 13,
FIVE_MINUTELY: 14,
TEN_MINUTELY: 15,
THIRTY_MINUTELY: 16,
HOURLY: 17,
TWO_HOURLY: 18,
SIX_HOURLY: 19,
DAILY: 20,
TWO_DAILY: 21,
WEEKLY: 22,
MONTHLY: 23,
QUARTERLY: 24,
BIANNUAL: 25,
ANNUAL: 26,
DECADAL: 27,
CENTENNIAL: 28,
NUM_GRANULARITIES: 29
};
exports.Granularity = Granularity;
@@ -4699,6 +4765,13 @@ var DateField = {
* @type {Array.<{datefield:number, step:number, spacing:number}>}
*/
var TICK_PLACEMENT = [];
TICK_PLACEMENT[Granularity.MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 1, spacing: 1 };
TICK_PLACEMENT[Granularity.TWO_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 2, spacing: 2 };
TICK_PLACEMENT[Granularity.FIVE_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 5, spacing: 5 };
TICK_PLACEMENT[Granularity.TEN_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 10, spacing: 10 };
TICK_PLACEMENT[Granularity.FIFTY_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 50, spacing: 50 };
TICK_PLACEMENT[Granularity.HUNDRED_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 100, spacing: 100 };
TICK_PLACEMENT[Granularity.FIVE_HUNDRED_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 500, spacing: 500 };
TICK_PLACEMENT[Granularity.SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 1, spacing: 1000 * 1 };
TICK_PLACEMENT[Granularity.TWO_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 2, spacing: 1000 * 2 };
TICK_PLACEMENT[Granularity.FIVE_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 5, spacing: 1000 * 5 };
@@ -4963,7 +5036,7 @@ var logRangeFraction = function logRangeFraction(r0, r1, pct) {
// Original calcuation:
// pct = (log(x) - log(xRange[0])) / (log(xRange[1]) - log(xRange[0])));
//
// Multiply both sides by the right-side demoninator.
// Multiply both sides by the right-side denominator.
// pct * (log(xRange[1] - log(xRange[0]))) = log(x) - log(xRange[0])
//
// add log(xRange[0]) to both sides
@@ -5231,7 +5304,7 @@ function isValidPoint(p, opt_allowNaNY) {
;
/**
* Number formatting function which mimicks the behavior of %g in printf, i.e.
* Number formatting function which mimics the behavior of %g in printf, i.e.
* either exponential or fixed format (without trailing 0s) is used depending on
* the length of the generated string. The advantage of this format is that
* there is a predictable upper bound on the resulting string length,
@@ -5385,7 +5458,7 @@ function hmsString_(hh, mm, ss, ms) {
/**
* Convert a JS date (millis since epoch) to a formatted string.
* @param {number} time The JavaScript time value (ms since epoch)
* @param {boolean} utc Wether output UTC or local time
* @param {boolean} utc Whether output UTC or local time
* @return {string} A date of one of these forms:
* "YYYY/MM/DD", "YYYY/MM/DD HH:MM" or "YYYY/MM/DD HH:MM:SS"
* @private
@@ -6293,6 +6366,12 @@ function dateAxisLabelFormatter(date, granularity, opts) {
if (frac === 0 || granularity >= DygraphTickers.Granularity.DAILY) {
// e.g. '21 Jan' (%d%b)
return zeropad(day) + '&#160;' + SHORT_MONTH_NAMES_[month];
} else if (granularity < DygraphTickers.Granularity.SECONDLY) {
// e.g. 40.310 (meaning 40 seconds and 310 milliseconds)
var str = "" + millis;
return zeropad(secs) + "." + ('000' + str).substring(str.length);
} else if (granularity > DygraphTickers.Granularity.MINUTELY) {
return hmsString_(hours, mins, secs, 0);
} else {
return hmsString_(hours, mins, secs, millis);
}
@@ -6372,7 +6451,7 @@ function dateValueFormatter(d, opts) {
* @param {Object} attrs Various other attributes, e.g. errorBars determines
* whether the input data contains error ranges. For a complete list of
* options, see http://dygraphs.com/options.html.
*/var Dygraph=function Dygraph(div,data,opts){this.__init__(div,data,opts);};Dygraph.NAME = "Dygraph";Dygraph.VERSION = "2.0.0"; // Various default values
*/var Dygraph=function Dygraph(div,data,opts){this.__init__(div,data,opts);};Dygraph.NAME = "Dygraph";Dygraph.VERSION = "2.1.0"; // Various default values
Dygraph.DEFAULT_ROLL_PERIOD = 1;Dygraph.DEFAULT_WIDTH = 480;Dygraph.DEFAULT_HEIGHT = 320; // For max 60 Hz. animation:
Dygraph.ANIMATION_STEPS = 12;Dygraph.ANIMATION_DURATION = 200; /**
* Standard plotters. These may be used by clients.
@@ -6643,9 +6722,9 @@ var target=e.target || e.fromElement;var relatedTarget=e.relatedTarget || e.toEl
// This happens when the graph is resized.
if(!this.resizeHandler_){this.resizeHandler_ = function(e){dygraph.resize();}; // Update when the window is resized.
// TODO(danvk): drop frames depending on complexity of the chart.
this.addAndTrackEvent(window,'resize',this.resizeHandler_);}};Dygraph.prototype.resizeElements_ = function(){this.graphDiv.style.width = this.width_ + "px";this.graphDiv.style.height = this.height_ + "px";var canvasScale=utils.getContextPixelRatio(this.canvas_ctx_);this.canvas_.width = this.width_ * canvasScale;this.canvas_.height = this.height_ * canvasScale;this.canvas_.style.width = this.width_ + "px"; // for IE
this.addAndTrackEvent(window,'resize',this.resizeHandler_);}};Dygraph.prototype.resizeElements_ = function(){this.graphDiv.style.width = this.width_ + "px";this.graphDiv.style.height = this.height_ + "px";var pixelRatioOption=this.getNumericOption('pixelRatio');var canvasScale=pixelRatioOption || utils.getContextPixelRatio(this.canvas_ctx_);this.canvas_.width = this.width_ * canvasScale;this.canvas_.height = this.height_ * canvasScale;this.canvas_.style.width = this.width_ + "px"; // for IE
this.canvas_.style.height = this.height_ + "px"; // for IE
if(canvasScale !== 1){this.canvas_ctx_.scale(canvasScale,canvasScale);}var hiddenScale=utils.getContextPixelRatio(this.hidden_ctx_);this.hidden_.width = this.width_ * hiddenScale;this.hidden_.height = this.height_ * hiddenScale;this.hidden_.style.width = this.width_ + "px"; // for IE
if(canvasScale !== 1){this.canvas_ctx_.scale(canvasScale,canvasScale);}var hiddenScale=pixelRatioOption || utils.getContextPixelRatio(this.hidden_ctx_);this.hidden_.width = this.width_ * hiddenScale;this.hidden_.height = this.height_ * hiddenScale;this.hidden_.style.width = this.width_ + "px"; // for IE
this.hidden_.style.height = this.height_ + "px"; // for IE
if(hiddenScale !== 1){this.hidden_ctx_.scale(hiddenScale,hiddenScale);}}; /**
* Detach DOM elements in the dygraph and null out all data references.
@@ -6801,6 +6880,7 @@ var oldValueRanges=this.yAxisRanges();var newValueRanges=[];for(var i=0;i < this
*/Dygraph.prototype.resetZoom = function(){var _this4=this;var dirtyX=this.isZoomed('x');var dirtyY=this.isZoomed('y');var dirty=dirtyX || dirtyY; // Clear any selection, since it's likely to be drawn in the wrong place.
this.clearSelection();if(!dirty)return; // Calculate extremes to avoid lack of padding on reset.
var _xAxisExtremes=this.xAxisExtremes();var _xAxisExtremes2=_slicedToArray(_xAxisExtremes,2);var minDate=_xAxisExtremes2[0];var maxDate=_xAxisExtremes2[1];var animatedZooms=this.getBooleanOption('animatedZooms');var zoomCallback=this.getFunctionOption('zoomCallback'); // TODO(danvk): merge this block w/ the code below.
// TODO(danvk): factor out a generic, public zoomTo method.
if(!animatedZooms){this.dateWindow_ = null;this.axes_.forEach(function(axis){if(axis.valueRange)delete axis.valueRange;});this.drawGraph_();if(zoomCallback){zoomCallback.call(this,minDate,maxDate,this.yAxisRanges());}return;}var oldWindow=null,newWindow=null,oldValueRanges=null,newValueRanges=null;if(dirtyX){oldWindow = this.xAxisRange();newWindow = [minDate,maxDate];}if(dirtyY){oldValueRanges = this.yAxisRanges();newValueRanges = this.yAxisExtremes();}this.doAnimatedZoom(oldWindow,newWindow,oldValueRanges,newValueRanges,function(){_this4.dateWindow_ = null;_this4.axes_.forEach(function(axis){if(axis.valueRange)delete axis.valueRange;});if(zoomCallback){zoomCallback.call(_this4,minDate,maxDate,_this4.yAxisRanges());}});}; /**
* Combined animation logic for all zoom functions.
* either the x parameters or y parameters may be null.
@@ -7217,7 +7297,7 @@ if('rollPeriod' in attrs){this.rollPeriod_ = attrs.rollPeriod;}if('dateWindow' i
// highlightCircleSize
// Check if this set options will require new points.
var requiresNewPoints=utils.isPixelChangingOptionList(this.attr_("labels"),attrs);utils.updateDeep(this.user_attrs_,attrs);this.attributes_.reparseSeries();if(file){ // This event indicates that the data is about to change, but hasn't yet.
// TODO(danvk): support cancelation of the update via this event.
// TODO(danvk): support cancellation of the update via this event.
this.cascadeEvents_('dataWillUpdate',{});this.file_ = file;if(!block_redraw)this.start_();}else {if(!block_redraw){if(requiresNewPoints){this.predraw_();}else {this.renderGraph_(false);}}}}; /**
* Make a copy of input attributes, removing file as a convenience.
* @private
@@ -8748,8 +8828,8 @@ rangeSelector.prototype.updateVisibility_ = function () {
* Resizes the range selector.
*/
rangeSelector.prototype.resize_ = function () {
function setElementRect(canvas, context, rect) {
var canvasScale = utils.getContextPixelRatio(context);
function setElementRect(canvas, context, rect, pixelRatioOption) {
var canvasScale = pixelRatioOption || utils.getContextPixelRatio(context);
canvas.style.top = rect.y + 'px';
canvas.style.left = rect.x + 'px';
@@ -8776,8 +8856,9 @@ rangeSelector.prototype.resize_ = function () {
h: this.getOption_('rangeSelectorHeight')
};
setElementRect(this.bgcanvas_, this.bgcanvas_ctx_, this.canvasRect_);
setElementRect(this.fgcanvas_, this.fgcanvas_ctx_, this.canvasRect_);
var pixelRatioOption = this.dygraph_.getNumericOption('pixelRatio');
setElementRect(this.bgcanvas_, this.bgcanvas_ctx_, this.canvasRect_, pixelRatioOption);
setElementRect(this.fgcanvas_, this.fgcanvas_ctx_, this.canvasRect_, pixelRatioOption);
};
/**

View File

@@ -1,14 +1,12 @@
var hours = 24;
var granularity = '2m';
var granularity = '5m';
var end = 0;
var graphdata = "https://bastart.spoton.cz/data/solar_monitor?range=24h&granularity=2m&end=0h";
var retention = 'monthly';
var graphdata = "https://bastart.spoton.cz/data/solar_monitor?range=24h&granularity=5m&end=0h&retention=monthly";
sol = new Dygraph(
// containing div
document.getElementById("solar"),
// CSV or path to a CSV file.
graphdata
,{
//labels: ['time','V_solar','Isolar', P_solar, P_cons],
axes : {
x : {
drawGrid: true,
@@ -16,80 +14,82 @@ graphdata
},
y : {
drawGrid: false,
drawAxis : true
drawAxis: true,
valueRange: [44.5,55]
},
y2 : {
drawGrid: false,
drawAxis: true,
independentTicks: true
independentTicks: true,
customBars: true,
valueRange: [0,1300]
}
},
rollPeriod: 5,
visibility: [true, false, true, true],
rollPeriod: 3,
interactionModel: {},
connectSeparatedPoints: true,
series:{
'V_solar': {
axis: 'y',
color: '#ffd020',
fillGraph: true,
fillAlpha: 0.4
},
'I_solar': {
axis: 'y',
color: '#ff1100'
},
'P_solar': {
axis: 'y2',
color: '#1111ff',
fillGraph: true,
fillAlpha: 0.4
color: '#ff5500'
},
'P_cons': {
axis: 'y2',
color: '#ff1111',
fillGraph: true,
fillAlpha: 0.4
'V_array': {
axis: 'y',
color: '#666'
}
},
ylabel: '<span style="color:#ffd020;">[V]</span>/<span style="color:#ff1100;">[A]</span>',
y2label: '<span style="color:#111177;">Solar / Consumption [W]</span>',
ylabel: '<span style="color:#666;">Battery [V]</span>',
y2label: '<span style="color:#ff2200;">Power [W]</span>',
labelsDiv: 'solar_labels',
legend: 'always'
legend: 'always',
customBars: true
}
);
function refreshGraph(){
graphdata = "https://bastart.spoton.cz/data/solar_monitor?range=" + hours + "h&granularity=" + granularity + "&end=" + end + "h";
graphdata = "https://bastart.spoton.cz/data/solar_monitor?range=" + hours + "h&granularity=" + granularity + "&end=" + end + "h&retention=" + retention;
sol.updateOptions({'file': graphdata});
//power.updateOptions({'file': graphdata});
}
function setHours(hours_to_set){
hours = hours_to_set;
switch(hours){
case '1':
granularity = '30s';
granularity = '10s';
retention = 'monthly';
break;
case '6':
granularity = '1m';
granularity = '10s';
retention = 'monthly';
break;
case '12':
granularity = '1m';
granularity = '2m';
retention = 'monthly';
break;
case '24':
granularity = '2m';
granularity = '5m';
retention = 'monthly';
break;
case '168':
granularity = '20m';
granularity = '15m';
retention = 'monthly';
break;
case '720':
granularity = '1h';
granularity = '3h';
retention = 'yearly';
break;
case '8760':
granularity = '6h';
retention = 'yearly';
break;
case '87600':
granularity = '24h';
retention = 'yearly';
break;
default:
granularity = '10m';
granularity = '5m';
retention = 'monthly';
}
end = 0;
//document.getElementById('xxx').innerHTML = graphdata;
@@ -118,3 +118,49 @@ function setForth(){
//document.getElementById('xxx').innerHTML = graphdata;
refreshGraph();
}
function getPageContents(callback,url,params) {
if (window.XMLHttpRequest){
// code for IE7+, Firefox, Chrome, Opera, Safari, SeaMonkey
xmlhttp=new XMLHttpRequest();
}
else{
// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
if(params!=null) {
xmlhttp.open("POST", url, true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
} else {
xmlhttp.open("GET", url, true);
}
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
callback(xmlhttp.responseText);
}
}
xmlhttp.send(params);
}
counter = 0;
function refreshValues(){
fresh_vals_url = '/data/solar_realtime_data?type=json'
getPageContents(function(result){freshVals=JSON.parse(result);},
fresh_vals_url)
document.getElementById('timestamp').innerHTML = freshVals.time;
document.getElementById('array_voltage').innerHTML = freshVals.V_array;
document.getElementById('array_percent').innerHTML = freshVals.perc_array;
document.getElementById('charge_current').innerHTML = freshVals.ChCurr;
document.getElementById('solar_power').innerHTML = freshVals.Psol;
document.getElementById('pmax_day').innerHTML = freshVals.Pmax_day;
counter = counter + 5000;
if(counter >= 360000){
refreshGraph();
document.getElementById('graph_timestamp').innerHTML = freshVals.time;
counter = 0;
}
}
var intervalVal = setInterval(refreshValues, 5000);

View File

@@ -1,16 +1,53 @@
var hours = 24;
var granularity = '1m';
var end = 0;
var winddata = "https://bastart.spoton.cz/data/wind_monitor?range=24h&granularity=1m&end=0h";
var temphumidata = "https://bastart.spoton.cz/data/temphumi_monitor?range=24h&granularity=2m&end=0h";
var pressuredata = "https://bastart.spoton.cz/data/pressure_monitor?range=168h&granularity=2m&end=0h";
//defaults
var default_granularity = '5m'; // needs to be specified as string, changes
var default_hours = 24; // int is OK, (h) hardcoded
var default_end = 0; // detto
var base_url = "https://bastart.spoton.cz/data/";
var counter = 0;
var tout = 5000; // fresh date refresh rate, in ms
var g_tout = 30000; // graph update rate, in ms
//initialize the uninitialized
var hours = new Object();
var granularity = new Object();
var end = new Object();
var graphdata = new Object();
var data = new Object();
// types match function names in python's Dynamic.py class
var types = Array('wind_monitor',
'temphumi_monitor',
'pressure_monitor',
'rrate_monitor');
// Now let's set up the defaults and primary data sources, leter changed dynamically
var types_size = types.length;
for(var i=0; i<types_size; i++){
var type = types[i];
hours[type] = default_hours;
granularity[type] = default_granularity;
end[type] = default_end;
if(type == 'wind_monitor'){
hours[type] = 12;
granularity[type] = '1m';
}
data[type] = base_url + type + "?range=" + hours[type] + "h&granularity=" + granularity[type] + "&end=" + default_end + "h";
if(type == 'pressure_monitor'){
hours[type] = 168;
granularity[type] = '1h';
data[type] = base_url + type + "?range=" + hours[type] + "h&granularity=" + granularity[type] + "&end=" + default_end + "h";
}
if(type == 'rrate_monitor'){
hours[type] = 168;
granularity[type] = '1h';
data[type] = base_url + type + "?range=" + hours[type] + "h&granularity=" + granularity[type] + "&end=" + default_end + "h";
}
}
wind = new Dygraph(
// containing div
document.getElementById("wind"),
// CSV or path to a CSV file.
winddata
,{
//labels: ['time','Speed','Gust', 'Direction'],
data['wind_monitor'],
{
axes : {
x : {
drawGrid: true,
@@ -27,7 +64,7 @@ winddata
independentTicks: true
}
},
rollPeriod: 20,
rollPeriod: 5,
interactionModel: {},
connectSeparatedPoints: true,
series:{
@@ -45,7 +82,6 @@ winddata
axis: 'y2',
color: '#999999'
}
},
ylabel: '<span style="color:#444444;">Speed<span style="color:#444444;"> / <span style="color:#ff5555;">Gusts</span> [km/h]',
y2label: '<span style="color:#999999;">Direction [&deg;]</span>',
@@ -55,12 +91,9 @@ winddata
);
temphumi_out = new Dygraph(
// containing div
document.getElementById("temphumi"),
// CSV or path to a CSV file.
temphumidata
,{
//labels: [time,T(ins),T(out),Humi(ins),Humi(out)],
data['temphumi_monitor'],
{
axes : {
x : {
drawGrid: true,
@@ -76,7 +109,7 @@ temphumidata
independentTicks: true
}
},
rollPeriod: 10,
rollPeriod: 5,
visibility: [false, true, false, true],
interactionModel: {},
connectSeparatedPoints: true,
@@ -101,7 +134,6 @@ temphumidata
axis: 'y2',
color: '#222288'
}
},
ylabel: '<span style="color:#555555;">Outside [&deg;C]</span>',
y2label: '<span style="color:#222288;">Humidity [%]</span>',
@@ -111,12 +143,9 @@ temphumidata
);
temphumi_in = new Dygraph(
// containing div
document.getElementById("temphumi_in"),
// CSV or path to a CSV file.
temphumidata
,{
//labels: [time,T(ins),T(out),Humi(ins),Humi(out)],
data['temphumi_monitor'],
{
axes : {
x : {
drawGrid: true,
@@ -134,7 +163,7 @@ temphumidata
valueRange: [0,100]
}
},
rollPeriod: 10,
rollPeriod: 5,
visibility: [true, false, true, false],
interactionModel: {},
connectSeparatedPoints: true,
@@ -159,7 +188,6 @@ temphumidata
axis: 'y2',
color: '#222288'
}
},
ylabel: '<span style="color:#555555;">Inside [&deg;C]</span>',
y2label: '<span style="color:#222288;">Humidity [%]</span>',
@@ -169,12 +197,9 @@ temphumidata
);
pressure = new Dygraph(
// containing div
document.getElementById("pressure"),
// CSV or path to a CSV file.
pressuredata
,{
//labels: [time,Pressure],
data['pressure_monitor'],
{
axes : {
x : {
drawGrid: true,
@@ -191,7 +216,7 @@ pressuredata
valueRange: [970,1055]
}
},
rollPeriod: 10,
rollPeriod: 20,
interactionModel: {},
connectSeparatedPoints: true,
visibility: [true, false],
@@ -208,7 +233,6 @@ pressuredata
fillGraph: true,
fillAlpha: 0.5
}
},
ylabel: '<span style="color:#555555;">Pressure [hPa]</span>',
labelsDiv: 'pressure_labels',
@@ -216,81 +240,175 @@ pressuredata
}
);
rrate = new Dygraph(
document.getElementById("rrate"),
data['rrate_monitor'],
{
axes : {
x : {
drawGrid: true,
drawAxis : true
},
y : {
drawGrid: false,
drawAxis : true,
valueRange: [0,3]
},
y2 : {
drawGrid: false,
drawAxis: true,
valueRange: [0,3]
}
},
rollPeriod: 0,
interactionModel: {},
connectSeparatedPoints: true,
visibility: [true, true],
series:{
'Rrate': {
axis: 'y',
color: '#0055ff',
fillGraph: true,
fillAlpha: 0.5
},
'Rrate_max': {
axis: 'y2',
color: '#0000aa',
fillGraph: false
}
},
ylabel: '<span style="color:#555555;">Rain rate [mm/h]</span>',
labelsDiv: 'rrate_labels',
legend: 'always'
}
);
// Functions for buttons to scale / move graphs
function refreshGraph(source){
graphdata = "https://bastart.spoton.cz/data/" + source + "?range=" + hours + "h&granularity=" + granularity + "&end=" + end + "h";
graphdata[source] = "https://bastart.spoton.cz/data/" + source + "?range=" + hours[source] + "h&granularity=" + granularity[source] + "&end=" + end[source] + "h";
if(source == 'wind_monitor'){
wind.updateOptions({'file': graphdata});
wind.updateOptions({'file': graphdata[source]});
}
if(source == 'temphumi_monitor'){
temphumi_out.updateOptions({'file': graphdata});
temphumi_in.updateOptions({'file': graphdata});
temphumi_out.updateOptions({'file': graphdata[source]});
temphumi_in.updateOptions({'file': graphdata[source]});
}
if(source == 'pressure_monitor'){
pressure.updateOptions({'file': graphdata});
pressure.updateOptions({'file': graphdata[source]});
}
//document.getElementById('xxx').innerHTML = source;
//power.updateOptions({'file': graphdata});
if(source == 'rrate_monitor'){
rrate.updateOptions({'file': graphdata[source]});
}
// TODO: adjust to update graph timestamps per click on ranges
// document.getElementById('g_tstamp_' + source).innerHTML = freshVals.time;
}
// here, target needs to match the python's dynamic class again
function setHours(hours_to_set, target){
hours = hours_to_set;
switch(hours){
hours[target] = hours_to_set;
switch(hours[target]){
case '1':
granularity = '1m';
if(target == 'temphumi_monitor'){ granularity = '2m';}
granularity[target] = '30s';
if(target == 'temphumi_monitor'){ granularity[target] = '2m';}
break;
case '6':
granularity = '1m';
if(target == 'temphumi_monitor'){ granularity = '2m';}
granularity[target] = '1m';
if(target == 'temphumi_monitor'){ granularity[target] = '2m';}
break;
case '12':
granularity = '1m';
if(target == 'temphumi_monitor'){ granularity = '2m';}
granularity[target] = '2m';
if(target == 'temphumi_monitor'){ granularity[target] = '2m';}
break;
case '24':
granularity = '1m';
if(target == 'temphumi_monitor'){ granularity = '2m';}
granularity[target] = '5m';
if(target == 'temphumi_monitor'){ granularity[target] = '2m';}
break;
case '168':
granularity = '10m';
granularity[target] = '10m';
break;
case '720':
granularity = '1h';
granularity[target] = '1h';
break;
case '4320':
granularity = '6h';
granularity[target] = '12h';
break;
case '8640':
granularity = '1d';
granularity[target] = '24h';
break;
default:
granularity = '10m';
granularity[target] = default_granularity;
}
end = 0;
//document.getElementById('xxx').innerHTML = target;
end[target] = 0;
refreshGraph(target);
}
// Functions for buttons to scale / move graphs
function setBack(target){
// range=1h -> range=2h&end=1h
disp_range = hours*1 - end*1;
hours = hours*1 + disp_range;
end = end*1 + disp_range;
//document.getElementById('xxx').innerHTML = graphdata;
disp_range = hours[target]*1 - end[target]*1;
hours[target] = hours[target]*1 + disp_range;
end[target] = end[target]*1 + disp_range;
refreshGraph(target);
}
function setForth(target){
disp_range = hours*1 - end*1;
hours = hours*1 - disp_range;
if(hours < disp_range){
hours = disp_range;
disp_range = hours[target]*1 - end[target]*1;
hours[target] = hours[target]*1 - disp_range;
if(hours[target] < disp_range){
hours[target] = disp_range;
}
end = end*1 - disp_range;
if(end < 0){
end = 0;
end[target] = end[target]*1 - disp_range;
if(end[target] < 0){
end[target] = 0;
}
//document.getElementById('xxx').innerHTML = graphdata;
refreshGraph(target);
}
function getPageContents(callback,url,params) {
if (window.XMLHttpRequest){
// code for IE7+, Firefox, Chrome, Opera, Safari, SeaMonkey
xmlhttp=new XMLHttpRequest();
}
else{
// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
if(params!=null) {
xmlhttp.open("POST", url, true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
} else {
xmlhttp.open("GET", url, true);
}
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
callback(xmlhttp.responseText);
}
}
xmlhttp.send(params);
}
function refreshValues(){
fresh_vals_url = '/data/weather_realtime_data?type=json'
getPageContents(function(result){freshVals=JSON.parse(result);},
fresh_vals_url)
document.getElementById('timestamp').innerHTML = freshVals.time;
document.getElementById('w_speed_km').innerHTML = freshVals.wind.speed;
document.getElementById('w_speed_ms').innerHTML = freshVals.wind.speed_ms;
document.getElementById('w_dir').innerHTML = freshVals.wind.dir;
document.getElementById('w_gust_km').innerHTML = freshVals.wind.gust;
document.getElementById('w_gust_ms').innerHTML = freshVals.wind.gust_ms;
document.getElementById('w_dir_name').innerHTML = freshVals.wind.dir_name;
document.getElementById('out_temp').innerHTML = freshVals.temperature.out;
document.getElementById('in_temp').innerHTML = freshVals.temperature.in;
document.getElementById('current_pressure').innerHTML = freshVals.pressure;
document.getElementById('current_rrate').innerHTML = freshVals.rainrate;
counter = counter + tout;
if(counter >= g_tout){
for(var i=0; i<types_size; i++){
var type = types[i];
refreshGraph(type);
document.getElementById('g_tstamp_' + type).innerHTML = freshVals.time;
}
counter = 0;
}
}
var intervalVal = setInterval(refreshValues, tout);

View File

@@ -1,6 +1,6 @@
<div id="footer">
Created in 101 minutes, in VI, while sitting in a train Wien -> Bratislava.
Uploeaded over EDGE with a lot of signal-less territory on roaming. Cheers.
Measurements utilise Arduino, ESP8266, lot of python, another lot
of Micropython, tons of "heart", no cocaine, but shitloads of Tea. Cheers.
Done by Ventil.
</div>
</body>

View File

@@ -10,14 +10,37 @@
<meta name="Keywords" content="automation, python, development, arduino, measurement">
<meta name="description" content="RasPi automation hardcore">
<meta name="Author" content="Spoton s.r.o. | Milan Toman">
<link rel="stylesheet" type="text/css" href="./static/css/def_style.css">
<link rel="stylesheet" type="text/css" href="https://bastart.spoton.cz/static/css/def_style.css">
<!-- Bootstrap core CSS -->
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
<link href="https://bastart.spoton.cz/static/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="../static/css/dashboard.css" rel="stylesheet">
<link href="https://bastart.spoton.cz/static/css/dashboard.css" rel="stylesheet">
<!-- Dygraph css and js -->
<link href="../static/css/dygraph.css" rel="stylesheet">
<script type="text/javascript" src="../static/js/dygraph.js"></script>
<link href="https://bastart.spoton.cz/static/css/dygraph.css" rel="stylesheet">
<script type="text/javascript" src="https://bastart.spoton.cz/static/js/dygraph.js"></script>
<link rel="icon" href="../../favicon.ico">
</head>
<body>
<!--
<script>
//window.fbAsyncInit = function() {
// FB.init({
// appId : '315766815677378',
// cookie : true,
// xfbml : true,
// version : 'v3.2'
// });
//
// FB.AppEvents.logPageView();
//
//};
//
//(function(d, s, id){
// var js, fjs = d.getElementsByTagName(s)[0];
// if (d.getElementById(id)) {return;}
// js = d.createElement(s); js.id = id;
// js.src = "https://connect.facebook.net/en_US/sdk.js";
// fjs.parentNode.insertBefore(js, fjs);
// }(document, 'script', 'facebook-jssdk'));
</script>-->

56
web/templates/status_admin.html Normal file → Executable file
View File

@@ -4,19 +4,35 @@
<div class="col"></div>
<main class="pt-4 col-10">
<h1>Status Dashboard</h1>
<h5>{timestamp} UTC</h5>
<h5 id="timestamp">{timestamp} UTC</h5>
<p>Up since: {_uptime}</p>
<section class="row text-center placeholders">
<div class="col placeholder">
<img src="{sol_icon}" width="20" class="img-fluid" alt="ISS solar irradiation">
<h3>{sol_value}</h3>
<h3 id="sol_val">{_sol_value}</h3>
<div class="text-muted">[V]</div>
</div>
<div class="col placeholder">
<img src="{cap_icon}" width="20" class="img-fluid" alt="ISS Capacitor state">
<h3>{cap_value}</h3>
<h3 id="cap_val">{_cap_value}</h3>
<div class="text-muted">[V]</div>
</div>
<div class="col placeholder">
<img src="{cpu_icon}" width="20" class="img-fluid" alt="CPU %">
<h3 id="cpu_val">{_cpu}</h3>
<div class="text-muted">[%]</div>
</div>
<div class="col placeholder">
<img src="{mem_icon}" width="20" class="img-fluid" alt="Mem / Disk %">
<h3><span id="mem">{_mem}</span> / <span id="disk">{_disk}</span></h3>
<div class="text-muted">[%]</div>
</div>
<div class="col placeholder">
<img src="{network_icon}" width="20" class="img-fluid" alt="RasPI network">
<h3><span id="net_in">{_net_in}</span> / <span id="net_out">{_net_out}</span></h3>
<div class="text-muted">IN [kB/s] / OUT [kB/s]</div>
</div>
</section>
<section class="row text-center placeholders">
<div class="col">
@@ -31,6 +47,7 @@
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('4320','solcap_monitor')">6Mo</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('8640','solcap_monitor')">Year</button>
<button type="button" class="btn btn-secondary" onclick="setForth('solcap_monitor')">&gt;&gt;</button>
<h6><span id="graph_timestamp">{timestamp}</span> UTC</h6>
</div>
</section>
<section class="row text-center placeholders">
@@ -47,8 +64,39 @@
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('4320','cpumem_monitor')">6Mo</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('8640','cpumem_monitor')">Year</button>
<button type="button" class="btn btn-secondary" onclick="setForth('cpumem_monitor')">&gt;&gt;</button>
<h6><span id="graph_timestamp">{timestamp}</span> UTC</h6>
</div>
<script type="text/javascript" src="../static/js/solcap_graph.js"></script>
</section>
<section class="row text-center placeholders">
<div class="col">
<div id="network_labels"></div>
<div id="network" class="container-fluid" style="width:100%;height:200px;"></div>
<button type="button" class="btn btn-secondary" onclick="setBack('network_monitor')">&lt;&lt;</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('1','network_monitor')">1h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('6','network_monitor')">6h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('12','network_monitor')">12h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('24','network_monitor')">24h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('168','network_monitor')">Week</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('720','network_monitor')">Month</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('4320','network_monitor')">6Mo</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('8640','network_monitor')">Year</button>
<button type="button" class="btn btn-secondary" onclick="setForth('network_monitor')">&gt;&gt;</button>
<h6><span id="graph_timestamp">{timestamp}</span> UTC</h6>
</div>
</section>
<section class="row text-center placeholders">
<div class="col">
<div id="esp_battery_labels"></div>
<div id="esp_battery" class="container-fluid" style="width:100%;height:200px;"></div>
<button type="button" class="btn btn-secondary" onclick="setBack('esp_battery_monitor')">&lt;&lt;</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('168','esp_battery_monitor')">Week</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('720','esp_battery_monitor')">Month</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('4320','esp_battery_monitor')">6Mo</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('8640','esp_battery_monitor')">Year</button>
<button type="button" class="btn btn-secondary" onclick="setForth('esp_battery_monitor')">&gt;&gt;</button>
<h6><span id="graph_timestamp">{timestamp}</span> UTC</h6>
</div>
<script type="text/javascript" src="../static/js/status_graph.js"></script>
</section>
</main>
<div class="col"></div>

View File

@@ -2,7 +2,7 @@
<button class="navbar-toggler navbar-toggler-right hidden-lg-up" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href="#">Monitoring</a>
<a class="navbar-brand" href="https://bastart.spoton.cz">Monitoring</a>
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
@@ -15,6 +15,9 @@
<li class="nav-item {status}">
<a class="nav-link" href="https://bastart.spoton.cz/status">Status</a>
</li>
<li class="nav-item {temphumi}">
<a class="nav-link" href="https://bastart.spoton.cz/temphumi">Temp / Humidity</a>
</li>
</ul>
</div>
</nav>

View File

@@ -4,31 +4,26 @@
<div class="col"></div>
<main class="pt-4 col-10">
<h1>Energy Dashboard</h1>
<h5><span id="timestamp">{timestamp}</span> UTC</h5>
<section class="row text-center placeholders">
<div class="col placeholder">
<img src="{array_voltage_icon}" width="20" class="img-fluid" alt="Battery voltage">
<h3>{array_voltage} [V]</h3>
<div class="text-muted">{array_percent} [%]</div>
<h3><span id="array_voltage">{array_voltage}</span> [V]</h3>
<div class="text-muted"><span id="array_percent">{array_percent}</span> [%]</div>
</div>
<div class="col placeholder">
<img src="../static/img/battery_current.png" width="20" class="img-fluid" alt="Battery current">
<h3>{charge_current}</h3>
<img src="{charge_current_icon}" width="20" class="img-fluid" alt="Battery current">
<h3><span id="charge_current">{charge_current}</span></h3>
<div class="text-muted">Charging [A]</div>
</div>
<div class="col placeholder">
<img src="../static/img/solar_power.png" width="20" class="img-fluid" alt="Solar array power">
<h3>{solar_power}</h3>
<img src="{solar_power_icon}" width="40" class="img-fluid" alt="Solar array power">
<h3><span id=solar_power>{solar_power}</span></h3>
<span class="text-muted"> Solar PV [W]</span>
</div>
<div class="col placeholder">
<img src="../static/img/solar_voltage.png" width="20" class="img-fluid" alt="Solar array voltage">
<h3>{voltage_solar}</h3>
<div class="text-muted">Solar [V]</div>
</div>
<div class="col placeholder">
<img src="../static/img/pmax_day.png" width="20" class="img-fluid" alt="Max power / day">
<h3>{pmax_day}</h3>
<img src="{solar_power_icon}" width="40" class="img-fluid" alt="Max power / day">
<h3><span id=pmax_day>{pmax_day}</span></h3>
<div class="text-muted">Pmax / day [W]</div>
</div>
</section>
@@ -45,19 +40,15 @@
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('24')">24h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('168')">Week</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('720')">Month</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('8760')">Year</button>
<button type="button" class="btn btn-secondary" onclick="setForth()">&gt;&gt;</button>
<h6><span id="graph_timestamp">{timestamp}</span> UTC</h6>
</div>
<div id="xxx"></div>
</section>
<div class="table-responisve">
<h2>Historical values by day</h2>
{history_table}
</div>
<div class="table-responsive">
{stats_table}
{fresh_table}
</div>
</main>
<div class="col"></div>

View File

@@ -4,40 +4,50 @@
<div class="col"></div>
<main class="pt-4 col-10">
<h1>Weather Dashboard</h1>
<h5>{timestamp} UTC</h5>
<h5><span id="timestamp">{_timestamp}</span> UTC</h5>
<section class="row text-center placeholders">
<div class="col-md-4 placeholder col-sm-6">
<img src="{w_speed_icon}" width="50" class="img-fluid" alt="Wind Speed">
<h4>{w_speed_km} [km/h]</h4>
<div class="text-muted">{w_speed_ms} [m/s]</div>
<img src="{_w_speed_icon}" width="50" class="img-fluid" alt="Wind Speed">
<h4><span id="w_speed_km">{_w_speed_km}</span> [km/h]</h4>
<div class="text-muted">
<span id="w_speed_ms">{_w_speed_ms}</span> [m/s]
</div>
</div>
<div class="col-md-4 placeholder col-sm-6">
<img src="{w_gust_icon}" width="50" class="img-fluid" alt="Wind Gusts">
<h4>{w_gust_km} [km/h]</h4>
<div class="text-muted">{w_gust_ms} [m/s]</div>
<img src="{_w_gust_icon}" width="50" class="img-fluid" alt="Wind Gusts">
<h4><span id="w_gust_km">{_w_gust_km} [km/h]</h4>
<div class="text-muted">
<span id="w_gust_ms">{_w_gust_ms}</span> [m/s]
</div>
</div>
<div class="col-md-4 placeholder col-sm-6">
<img src="{w_dir_icon}" width="50" class="img-fluid" alt="Wind Direction">
<h4>{w_dir_name}</h4>
<div class="text-muted">{w_dir_deg} &deg;</div>
<img src="{_w_dir_icon}" width="50" class="img-fluid" alt="Wind Direction">
<h4><span id="w_dir_name">{_w_dir_name}</h4>
<div class="text-muted"><span id="w_dir">{_w_dir_deg}</span> &deg;</div>
</div>
</section>
<section class="row text-center placeholders">
<div class="col-md-4 placeholder col-sm-6">
<img src="{out_temp_icon}" width="50" class="img-fluid" alt="Outside Temperature">
<h4>{out_temp} &deg;C</h4>
<div class="col-md-3 placeholder col-sm-6">
<img src="{_out_temp_icon}" width="30" class="img-fluid" alt="Outside Temperature">
<h4><span id="out_temp">{_out_temp}</span> &deg;C</h4>
<div class="text-muted">[Outside]</div>
</div>
<div class="col-md-4 placeholder col-sm-6">
<img src="{in_temp_icon}" width="50" class="img-fluid" alt="Inside Temperature">
<h4>{in_temp} &deg;C</h4>
<div class="col-md-3 placeholder col-sm-6">
<img src="{_in_temp_icon}" width="30" class="img-fluid" alt="Inside Temperature">
<h4><span id="in_temp">{_in_temp}</span> &deg;C</h4>
<div class="text-muted">[Inside]</div>
</div>
<div class="col-md-4 placeholder col-sm-6">
<img src="{pressure_icon}" width="50" class="img-fluid" alt="Atmospheric pressure">
<h4>{pressure} [hPa]</h4>
<div class="text-muted">{raw_pressure} [hPa] raw</div>
<div class="col-md-3 placeholder col-sm-6">
<img src="{_pressure_icon}" width="30" class="img-fluid" alt="Atmospheric pressure">
<h4><span id="current_pressure">{_pressure}</span> [hPa]</h4>
<div class="text-muted">{_raw_pressure} [hPa] raw</div>
</div>
<div class="col-md-3 placeholder col-sm-6">
<img src="{_rrate_icon}" width="30" class="img-fluid" alt="Rain rate">
<h4><span id="current_rrate">{_rrate}</span></h4>
<div class="text-muted">[mm/h]</div>
</div>
</section>
@@ -56,6 +66,7 @@
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('4320','wind_monitor')">6Mo</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('8640','wind_monitor')">Year</button>
<button type="button" class="btn btn-secondary" onclick="setForth('wind_monitor')">&gt;&gt;</button>
<h6><span id="g_tstamp_wind_monitor">{_timestamp}</span> UTC</h6>
</div></div>
</div>
</section>
@@ -74,6 +85,7 @@
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('4320','temphumi_monitor')">6Mo</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('8640','temphumi_monitor')">Year</button>
<button type="button" class="btn btn-secondary" onclick="setForth('temphumi_monitor')">&gt;&gt;</button>
<h6><span id="g_tstamp_temphumi_monitor">{_timestamp}</span> UTC</h6>
</div>
</section>
<section class="row text-center placeholders">
@@ -88,6 +100,22 @@
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('4320','pressure_monitor')">6Mo</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('8640','pressure_monitor')">Year</button>
<button type="button" class="btn btn-secondary" onclick="setForth('pressure_monitor')">&gt;&gt;</button>
<h6><span id="g_tstamp_pressure_monitor">{_timestamp}</span> UTC</h6>
</div>
</section>
<section class="row text-center placeholders">
<div class="col">
<div id="rrate_labels"></div>
<div id="rrate" class="container-fluid" style="width:100%;height:200px;"></div>
<button type="button" class="btn btn-secondary" onclick="setBack('rrate_monitor')">&lt;&lt;</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('12','rrate_monitor')">12h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('24','rrate_monitor')">24h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('168','rrate_monitor')">Week</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('720','rrate_monitor')">Month</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('4320','rrate_monitor')">6Mo</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('8640','rrate_monitor')">Year</button>
<button type="button" class="btn btn-secondary" onclick="setForth('rrate_monitor')">&gt;&gt;</button>
<h6><span id="g_tstamp_rrate_monitor">{_timestamp}</span> UTC</h6>
</div>
<script type="text/javascript" src="../static/js/weather_graph.js"></script>
</section>

View File

@@ -1,280 +0,0 @@
<div class="container-fluid">
<div class="row">
<!--
--------------------------- Nahodou menu na levo -----------------------
--
<nav class="col-sm-3 col-md-2 hidden-xs-down bg-faded sidebar">
<ul class="nav nav-pills flex-column">
<li class="nav-item">
<a class="nav-link active" href="#">Overview <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Reports</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Analytics</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Export</a>
</li>
</ul>
<ul class="nav nav-pills flex-column">
<li class="nav-item">
<a class="nav-link" href="#">Nav item</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Nav item again</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">One more nav</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Another nav item</a>
</li>
</ul>
<ul class="nav nav-pills flex-column">
<li class="nav-item">
<a class="nav-link" href="#">Nav item again</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">One more nav</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Another nav item</a>
</li>
</ul>
</nav>
-->
<div class="col"></div>
<main class="pt-4 col-10">
<h1>Weather Dashboard</h1>
<h5>{timestamp}</h5>
<section class="row text-center placeholders">
<div class="col-md-4 placeholder col-sm-6">
<img src="{w_speed_icon}" width="50" class="img-fluid" alt="Wind Speed">
<h4>{w_speed_km} [km/h]</h4>
<div class="text-muted">{w_speed_ms} [m/s]</div>
</div>
<div class="col-md-4 placeholder col-sm-6">
<img src="{w_gust_icon}" width="50" class="img-fluid" alt="Wind Gusts">
<h4>{w_gust_km} [km/h]</h4>
<div class="text-muted">{w_gust_ms} [m/s]</div>
</div>
<div class="col-md-4 placeholder col-sm-6">
<img src="{w_dir_icon}" width="50" class="img-fluid" alt="Wind Direction">
<h4>{w_dir_name}</h4>
<div class="text-muted">{w_dir_deg} &deg;</div>
</section>
<section class="row text-center placeholders">
<div class="col-md-4 placeholder col-sm-6">
<img src="{out_temp_icon}" width="50" class="img-fluid" alt="Outside Temperature">
<h4>{out_temp} &deg;C</h4>
<div class="text-muted">[Outside]</div>
</div>
<div class="col-md-4 placeholder col-sm-6">
<img src="{in_temp_icon}" width="50" class="img-fluid" alt="Inside Temperature">
<h4>{in_temp} &deg;C</h4>
<div class="text-muted">[Inside]</div>
</div>
<div class="col-md-4 placeholder col-sm-6">
<img src="{pressure_icon}" width="50" class="img-fluid" alt="Atmospheric pressure">
<h4>{pressure} [hPa] Adjusted</h4>
<div class="text-muted">{raw_pressure} [hPa] Actual</div>
</section>
<section class="row text-center placeholders">
<div class="col">
<h2>Wind speed and direction</h2>
<div id="wind_labels"></div>
<div id="wind" class="container-fluid" style="width:100%;height:200px;"></div>
<div class="row"><div class="col py-5">
<button type="button" class="btn btn-secondary" onclick="setBack('wind_monitor')">&lt;&lt;</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('1','wind_monitor')">1h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('6','wind_monitor')">6h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('12','wind_monitor')">12h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('24','wind_monitor')">24h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('168','wind_monitor')">Week</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('5040','wind_monitor')">Month</button>
<button type="button" class="btn btn-secondary" onclick="setForth('wind_monitor')">&gt;&gt;</button>
</div></div>
</div>
</section>
<section class="row text-center placeholders">
<div class="col">
<h2>Temperature and Humidity</h2>
<div id="temphumi_labels"></div><div id="temphumi_labels_in"></div>
<div id="temphumi" class="container-fluid" style="width:100%;height:200px;"></div>
<div id="temphumi_in" class="container-fluid" style="width:100%;height:200px;"></div>
<button type="button" class="btn btn-secondary" onclick="setBack('temphumi_monitor')">&lt;&lt;</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('1','temphumi_monitor')">1h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('6','temphumi_monitor')">6h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('12','temphumi_monitor')">12h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('24','temphumi_monitor')">24h</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('168','temphumi_monitor')">Week</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="setHours('5040','temphumi_monitor')">Month</button>
<<<<<<< HEAD
<button type="button" class="btn btn-secondary" onclick="setForth('temphumi_monitor');">&gt;&gt;</button>
=======
<button type="button" class="btn btn-secondary" onclick="setForth('temphumi_monitor')">&gt;&gt;</button>
>>>>>>> ca62f04757f1010262e5559bbd8753bfa49bf34e
</div>
</section>
<section class="row text-center placeholders">
<div class="col">
<div id="pressure_labels"></div>
<div id="pressure" class="container-fluid" style="width:100%;"></div>
</div>
<script type="text/javascript" src="../static/js/weather_graph.js"></script>
</section>
</main>
<div class="col" id="xxx"></div>
<!--
<h2>Section title</h2>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Header</th>
<th>Header</th>
<th>Header</th>
<th>Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>1,001</td>
<td>Lorem</td>
<td>ipsum</td>
<td>dolor</td>
<td>sit</td>
</tr>
<tr>
<td>1,002</td>
<td>amet</td>
<td>consectetur</td>
<td>adipiscing</td>
<td>elit</td>
</tr>
<tr>
<td>1,003</td>
<td>Integer</td>
<td>nec</td>
<td>odio</td>
<td>Praesent</td>
</tr>
<tr>
<td>1,003</td>
<td>libero</td>
<td>Sed</td>
<td>cursus</td>
<td>ante</td>
</tr>
<tr>
<td>1,004</td>
<td>dapibus</td>
<td>diam</td>
<td>Sed</td>
<td>nisi</td>
</tr>
<tr>
<td>1,005</td>
<td>Nulla</td>
<td>quis</td>
<td>sem</td>
<td>at</td>
</tr>
<tr>
<td>1,006</td>
<td>nibh</td>
<td>elementum</td>
<td>imperdiet</td>
<td>Duis</td>
</tr>
<tr>
<td>1,007</td>
<td>sagittis</td>
<td>ipsum</td>
<td>Praesent</td>
<td>mauris</td>
</tr>
<tr>
<td>1,008</td>
<td>Fusce</td>
<td>nec</td>
<td>tellus</td>
<td>sed</td>
</tr>
<tr>
<td>1,009</td>
<td>augue</td>
<td>semper</td>
<td>porta</td>
<td>Mauris</td>
</tr>
<tr>
<td>1,010</td>
<td>massa</td>
<td>Vestibulum</td>
<td>lacinia</td>
<td>arcu</td>
</tr>
<tr>
<td>1,011</td>
<td>eget</td>
<td>nulla</td>
<td>Class</td>
<td>aptent</td>
</tr>
<tr>
<td>1,012</td>
<td>taciti</td>
<td>sociosqu</td>
<td>ad</td>
<td>litora</td>
</tr>
<tr>
<td>1,013</td>
<td>torquent</td>
<td>per</td>
<td>conubia</td>
<td>nostra</td>
</tr>
<tr>
<td>1,014</td>
<td>per</td>
<td>inceptos</td>
<td>himenaeos</td>
<td>Curabitur</td>
</tr>
<tr>
<td>1,015</td>
<td>sodales</td>
<td>ligula</td>
<td>in</td>
<td>libero</td>
</tr>
</tbody>
</table>
</div>
-->
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
<script src="../static/js/bootstrap.min.js"></script>
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="../static/js/ie10bugfix.js"></script>