From 47326ed774b2bc71f8abc18e10b911e296c48629 Mon Sep 17 00:00:00 2001
From: Milan Toman
Date: Wed, 21 Aug 2019 15:44:49 +0200
Subject: [PATCH] New, updated version of web. Might contain customizations
---
web/chttpd.py | 84 ++++----
web/static/js/dygraph.js | 189 ++++++++++++-----
web/static/js/solar_graph.js | 126 ++++++++----
web/static/js/weather_graph.js | 260 +++++++++++++++++-------
web/templates/footer.html | 4 +-
web/templates/header.html | 33 ++-
web/templates/status_admin.html | 56 +++++-
web/templates/top_menu.html | 5 +-
web/templates/voltage_admin.html | 31 +--
web/templates/weather_admin.html | 68 +++++--
web/templates/weather_admin.html.orig | 280 --------------------------
11 files changed, 600 insertions(+), 536 deletions(-)
mode change 100644 => 100755 web/templates/status_admin.html
delete mode 100644 web/templates/weather_admin.html.orig
diff --git a/web/chttpd.py b/web/chttpd.py
index 44da264..ee303a9 100755
--- a/web/chttpd.py
+++ b/web/chttpd.py
@@ -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
+
+ Contains basic server settings and how the sub-modules
+ are called and mounted to their respective paths
-# 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)
+ Args:
+ *None*
-# If launched directly, let's go
-if __name__ == '__main__':
+ 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
diff --git a/web/static/js/dygraph.js b/web/static/js/dygraph.js
index 012faa1..dff393a 100644
--- a/web/static/js/dygraph.js
+++ b/web/static/js/dygraph.js
@@ -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');
@@ -355,7 +407,7 @@ module.exports = exports["default"];
*/
/**
- * @fileoverview DataHandler implementation for the combination
+ * @fileoverview DataHandler implementation for the combination
* of error bars and fractions options.
* @author David Eberlein (david.eberlein@ch.sauter-bc.com)
*/
@@ -475,7 +527,7 @@ module.exports = exports["default"];
*/
/**
- * @fileoverview DataHandler base implementation for the "bar"
+ * @fileoverview DataHandler base implementation for the "bar"
* data formats. This implementation must be extended and the
* extractSeries and rollingAverage must be implemented.
* @author David Eberlein (david.eberlein@ch.sauter-bc.com)
@@ -512,13 +564,13 @@ BarsHandler.prototype = new _datahandler2['default']();
// (I get closure compiler errors if this isn't here.)
/**
* @override
- * @param {!Array.} rawData The raw data passed into dygraphs where
+ * @param {!Array.} rawData The raw data passed into dygraphs where
* rawData[i] = [x,ySeries1,...,ySeriesN].
* @param {!number} seriesIndex Index of the series to extract. All other
* series should be ignored.
* @param {!DygraphOptions} options Dygraph options.
* @return {Array.<[!number,?number,?]>} The series in the unified data format
- * where series[i] = [x,y,{extras}].
+ * where series[i] = [x,y,{extras}].
*/
BarsHandler.prototype.extractSeries = function (rawData, seriesIndex, options) {
// Not implemented here must be extended
@@ -526,7 +578,7 @@ BarsHandler.prototype.extractSeries = function (rawData, seriesIndex, options) {
/**
* @override
- * @param {!Array.<[!number,?number,?]>} series The series in the unified
+ * @param {!Array.<[!number,?number,?]>} series The series in the unified
* data format where series[i] = [x,y,{extras}].
* @param {!number} rollPeriod The number of points over which to average the data
* @param {!DygraphOptions} options The dygraph options.
@@ -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) + ' ' + 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);
};
/**
diff --git a/web/static/js/solar_graph.js b/web/static/js/solar_graph.js
index 559d935..c4a0bf6 100644
--- a/web/static/js/solar_graph.js
+++ b/web/static/js/solar_graph.js
@@ -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: '[V]/[A]',
- y2label: 'Solar / Consumption [W]',
+ ylabel: 'Battery [V]',
+ y2label: 'Power [W]',
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);
diff --git a/web/static/js/weather_graph.js b/web/static/js/weather_graph.js
index d6c4aaf..d73dff1 100644
--- a/web/static/js/weather_graph.js
+++ b/web/static/js/weather_graph.js
@@ -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; iSpeed / Gusts [km/h]',
y2label: 'Direction [°]',
@@ -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: 'Outside [°C]',
y2label: 'Humidity [%]',
@@ -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: 'Inside [°C]',
y2label: 'Humidity [%]',
@@ -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: 'Pressure [hPa]',
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: 'Rain rate [mm/h]',
+ 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
+
+
diff --git a/web/templates/status_admin.html b/web/templates/status_admin.html
old mode 100644
new mode 100755
index 8810aef..2317890
--- a/web/templates/status_admin.html
+++ b/web/templates/status_admin.html
@@ -4,19 +4,35 @@
Status Dashboard
- {timestamp} UTC
+ {timestamp} UTC
+ Up since: {_uptime}

-
{sol_value}
+
{_sol_value}
[V]

-
{cap_value}
+
{_cap_value}
[V]
+
+

+
{_cpu}
+
[%]
+
+
+

+
{_mem} / {_disk}
+
[%]
+
+
+

+
{_net_in} / {_net_out}
+
IN [kB/s] / OUT [kB/s]
+
@@ -31,6 +47,7 @@
+
{timestamp} UTC
@@ -47,8 +64,39 @@
+ {timestamp} UTC
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{timestamp} UTC
+
+
+
+
+
+
+
+
+
+
+
+
+
{timestamp} UTC
+
+
diff --git a/web/templates/top_menu.html b/web/templates/top_menu.html
index 1b7b363..5b25a8a 100644
--- a/web/templates/top_menu.html
+++ b/web/templates/top_menu.html
@@ -2,7 +2,7 @@
- Monitoring
+ Monitoring
diff --git a/web/templates/voltage_admin.html b/web/templates/voltage_admin.html
index 48408f2..b3660f8 100644
--- a/web/templates/voltage_admin.html
+++ b/web/templates/voltage_admin.html
@@ -4,31 +4,26 @@
Energy Dashboard
-
+ {timestamp} UTC

-
{array_voltage} [V]
-
{array_percent} [%]
+
{array_voltage} [V]
+
{array_percent} [%]
-

-
{charge_current}
+

+
{charge_current}
Charging [A]
-

-
{solar_power}
+

+
{solar_power}
Solar PV [W]
-

-
{voltage_solar}
-
Solar [V]
-
-
-

-
{pmax_day}
+

+
{pmax_day}
Pmax / day [W]
@@ -45,19 +40,15 @@
+
+ {timestamp} UTC
-
Historical values by day
- {history_table}
-
-
{stats_table}
- {fresh_table}
-
diff --git a/web/templates/weather_admin.html b/web/templates/weather_admin.html
index 9bda8d5..b68f916 100644
--- a/web/templates/weather_admin.html
+++ b/web/templates/weather_admin.html
@@ -4,40 +4,50 @@
Weather Dashboard
- {timestamp} UTC
+ {_timestamp} UTC
-

-
{w_speed_km} [km/h]
-
{w_speed_ms} [m/s]
+

+
{_w_speed_km} [km/h]
+
+ {_w_speed_ms} [m/s]
+
-

-
{w_gust_km} [km/h]
-
{w_gust_ms} [m/s]
+

+
{_w_gust_km} [km/h]
+
+ {_w_gust_ms} [m/s]
+
-

-
{w_dir_name}
-
{w_dir_deg} °
+

+
{_w_dir_name}
+
{_w_dir_deg} °
+
-
-

-
{out_temp} °C
+
+

+
{_out_temp} °C
[Outside]
-
-

-
{in_temp} °C
+
+

+
{_in_temp} °C
[Inside]
-
-

-
{pressure} [hPa]
-
{raw_pressure} [hPa] raw
+
+

+
{_pressure} [hPa]
+
{_raw_pressure} [hPa] raw
+
+
+

+
{_rrate}
+
[mm/h]
@@ -56,6 +66,7 @@
+
{_timestamp} UTC
@@ -74,6 +85,7 @@
+ {_timestamp} UTC
@@ -88,6 +100,22 @@
+ {_timestamp} UTC
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{_timestamp} UTC
diff --git a/web/templates/weather_admin.html.orig b/web/templates/weather_admin.html.orig
deleted file mode 100644
index 07c77f1..0000000
--- a/web/templates/weather_admin.html.orig
+++ /dev/null
@@ -1,280 +0,0 @@
-
-
-
-
-
-
- Weather Dashboard
- {timestamp}
-
-
-
-

-
{w_speed_km} [km/h]
-
{w_speed_ms} [m/s]
-
-
-

-
{w_gust_km} [km/h]
-
{w_gust_ms} [m/s]
-
-
-

-
{w_dir_name}
-
{w_dir_deg} °
-
-
-
-
-

-
{out_temp} °C
-
[Outside]
-
-
-

-
{in_temp} °C
-
[Inside]
-
-
-

-
{pressure} [hPa] Adjusted
-
{raw_pressure} [hPa] Actual
-
-
-
-
-
Wind speed and direction
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Temperature and Humidity
-
-
-
-
-
-
-
-
-
-
-<<<<<<< HEAD
-
-=======
-
->>>>>>> ca62f04757f1010262e5559bbd8753bfa49bf34e
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-